Turn a NOTICE into a DEBUG message.
[asterisk-bristuff.git] / apps / app_voicemail.c
blob59e751c4d0b5b385bc8e921158bf977344f5f7a2
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];
113 static char imapserver[48];
114 static char imapport[8];
115 static char imapflags[128];
116 static char imapfolder[64];
117 static char authuser[32];
118 static char authpassword[42];
120 static int expungeonhangup = 1;
121 static char delimiter = '\0';
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 vm_state *vms);
131 static char *get_user_by_mailbox(char *mailbox);
132 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
133 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
134 static void vmstate_insert(struct vm_state *vms);
135 static void vmstate_delete(struct vm_state *vms);
136 static void set_update(MAILSTREAM * stream);
137 static void init_vm_state(struct vm_state *vms);
138 static void check_msgArray(struct vm_state *vms);
139 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
140 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
141 static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num);
142 static void get_mailbox_delimiter(MAILSTREAM *stream);
143 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
144 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
145 static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms);
146 static void check_quota(struct vm_state *vms, char *mailbox);
147 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box);
148 struct vmstate {
149 struct vm_state *vms;
150 struct vmstate *next;
152 AST_MUTEX_DEFINE_STATIC(vmstate_lock);
153 static struct vmstate *vmstates = NULL;
154 #endif
156 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
158 #define COMMAND_TIMEOUT 5000
159 /* Don't modify these here; set your umask at runtime instead */
160 #define VOICEMAIL_DIR_MODE 0777
161 #define VOICEMAIL_FILE_MODE 0666
162 #define CHUNKSIZE 65536
164 #define VOICEMAIL_CONFIG "voicemail.conf"
165 #define ASTERISK_USERNAME "asterisk"
167 /* Default mail command to mail voicemail. Change it with the
168 mailcmd= command in voicemail.conf */
169 #define SENDMAIL "/usr/sbin/sendmail -t"
171 #define INTRO "vm-intro"
173 #define MAXMSG 100
174 #ifndef IMAP_STORAGE
175 #define MAXMSGLIMIT 9999
176 #else
177 #define MAXMSGLIMIT 255
178 #endif
180 #define BASEMAXINLINE 256
181 #define BASELINELEN 72
182 #define BASEMAXINLINE 256
183 #define eol "\r\n"
185 #define MAX_DATETIME_FORMAT 512
186 #define MAX_NUM_CID_CONTEXTS 10
188 #define VM_REVIEW (1 << 0)
189 #define VM_OPERATOR (1 << 1)
190 #define VM_SAYCID (1 << 2)
191 #define VM_SVMAIL (1 << 3)
192 #define VM_ENVELOPE (1 << 4)
193 #define VM_SAYDURATION (1 << 5)
194 #define VM_SKIPAFTERCMD (1 << 6)
195 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
196 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
197 #define VM_PBXSKIP (1 << 9)
198 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
199 #define VM_ATTACH (1 << 11)
200 #define VM_DELETE (1 << 12)
201 #define VM_ALLOCED (1 << 13)
202 #define VM_SEARCH (1 << 14)
203 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
204 #define ERROR_LOCK_PATH -100
205 #define ERROR_MAILBOX_FULL -200
208 enum {
209 OPT_SILENT = (1 << 0),
210 OPT_BUSY_GREETING = (1 << 1),
211 OPT_UNAVAIL_GREETING = (1 << 2),
212 OPT_RECORDGAIN = (1 << 3),
213 OPT_PREPEND_MAILBOX = (1 << 4),
214 OPT_PRIORITY_JUMP = (1 << 5),
215 OPT_AUTOPLAY = (1 << 6),
216 } vm_option_flags;
218 enum {
219 OPT_ARG_RECORDGAIN = 0,
220 OPT_ARG_PLAYFOLDER = 1,
221 /* This *must* be the last value in this enum! */
222 OPT_ARG_ARRAY_SIZE = 2,
223 } vm_option_args;
225 AST_APP_OPTIONS(vm_app_options, {
226 AST_APP_OPTION('s', OPT_SILENT),
227 AST_APP_OPTION('b', OPT_BUSY_GREETING),
228 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
229 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
230 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
231 AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
232 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
235 static int load_config(void);
237 /*! \page vmlang Voicemail Language Syntaxes Supported
239 \par Syntaxes supported, not really language codes.
240 \arg \b en - English
241 \arg \b de - German
242 \arg \b es - Spanish
243 \arg \b fr - French
244 \arg \b it = Italian
245 \arg \b nl - Dutch
246 \arg \b pt - Polish
247 \arg \b pt - Portuguese
248 \arg \b pt_BR - Portuguese (Brazil)
249 \arg \b gr - Greek
250 \arg \b no - Norwegian
251 \arg \b se - Swedish
252 \arg \b ua - Ukrainian
254 German requires the following additional soundfile:
255 \arg \b 1F einE (feminine)
257 Spanish requires the following additional soundfile:
258 \arg \b 1M un (masculine)
260 Dutch, Portuguese & Spanish require the following additional soundfiles:
261 \arg \b vm-INBOXs singular of 'new'
262 \arg \b vm-Olds singular of 'old/heard/read'
264 NB these are plural:
265 \arg \b vm-INBOX nieuwe (nl)
266 \arg \b vm-Old oude (nl)
268 Polish uses:
269 \arg \b vm-new-a 'new', feminine singular accusative
270 \arg \b vm-new-e 'new', feminine plural accusative
271 \arg \b vm-new-ych 'new', feminine plural genitive
272 \arg \b vm-old-a 'old', feminine singular accusative
273 \arg \b vm-old-e 'old', feminine plural accusative
274 \arg \b vm-old-ych 'old', feminine plural genitive
275 \arg \b digits/1-a 'one', not always same as 'digits/1'
276 \arg \b digits/2-ie 'two', not always same as 'digits/2'
278 Swedish uses:
279 \arg \b vm-nytt singular of 'new'
280 \arg \b vm-nya plural of 'new'
281 \arg \b vm-gammalt singular of 'old'
282 \arg \b vm-gamla plural of 'old'
283 \arg \b digits/ett 'one', not always same as 'digits/1'
285 Norwegian uses:
286 \arg \b vm-ny singular of 'new'
287 \arg \b vm-nye plural of 'new'
288 \arg \b vm-gammel singular of 'old'
289 \arg \b vm-gamle plural of 'old'
291 Dutch also uses:
292 \arg \b nl-om 'at'?
294 Spanish also uses:
295 \arg \b vm-youhaveno
297 Ukrainian requires the following additional soundfile:
298 \arg \b vm-nove 'nove'
299 \arg \b vm-stare 'stare'
300 \arg \b digits/ua/1e 'odne'
302 Italian requires the following additional soundfile:
304 For vm_intro_it:
305 \arg \b vm-nuovo new
306 \arg \b vm-nuovi new plural
307 \arg \b vm-vecchio old
308 \arg \b vm-vecchi old plural
310 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
311 spelled among others when you have to change folder. For the above reasons, vm-INBOX
312 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
316 struct baseio {
317 int iocp;
318 int iolen;
319 int linelength;
320 int ateof;
321 unsigned char iobuf[BASEMAXINLINE];
324 /*! Structure for linked list of users */
325 struct ast_vm_user {
326 char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
327 char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
328 char password[80]; /*!< Secret pin code, numbers only */
329 char fullname[80]; /*!< Full name, for directory app */
330 char email[80]; /*!< E-mail address */
331 char pager[80]; /*!< E-mail address to pager (no attachment) */
332 char serveremail[80]; /*!< From: Mail address */
333 char mailcmd[160]; /*!< Configurable mail command */
334 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
335 char zonetag[80]; /*!< Time zone */
336 char callback[80];
337 char dialout[80];
338 char uniqueid[80]; /*!< Unique integer identifier */
339 char exit[80];
340 char attachfmt[20]; /*!< Attachment format */
341 unsigned int flags; /*!< VM_ flags */
342 int saydurationm;
343 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
344 #ifdef IMAP_STORAGE
345 char imapuser[80]; /* IMAP server login */
346 char imappassword[80]; /* IMAP server password if authpassword not defined */
347 #endif
348 double volgain; /*!< Volume gain for voicemails sent via email */
349 AST_LIST_ENTRY(ast_vm_user) list;
352 struct vm_zone {
353 AST_LIST_ENTRY(vm_zone) list;
354 char name[80];
355 char timezone[80];
356 char msg_format[512];
359 struct vm_state {
360 char curbox[80];
361 char username[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 int updated; /* decremented on each mail check until 1 -allows delay */
376 long msgArray[256];
377 MAILSTREAM *mailstream;
378 int vmArrayIndex;
379 char imapuser[80]; /* IMAP server login */
380 int interactive;
381 unsigned int quota_limit;
382 unsigned int quota_usage;
383 struct vm_state *persist_vms;
384 #endif
386 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);
387 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
388 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
389 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
390 signed char record_gain, struct vm_state *vms);
391 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
392 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
393 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
394 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);
395 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
396 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
397 #endif
398 static void apply_options(struct ast_vm_user *vmu, const char *options);
400 #ifdef ODBC_STORAGE
401 static char odbc_database[80];
402 static char odbc_table[80];
403 #define RETRIEVE(a,b) retrieve_file(a,b)
404 #define DISPOSE(a,b) remove_file(a,b)
405 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
406 #define EXISTS(a,b,c,d) (message_exists(a,b))
407 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
408 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
409 #define DELETE(a,b,c) (delete_file(a,b))
410 #else
411 #ifdef IMAP_STORAGE
412 #define RETRIEVE(a,b)
413 #define DISPOSE(a,b)
414 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
415 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
416 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
417 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
418 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
419 #define DELETE(a,b,c) (vm_delete(c))
420 #else
421 #define RETRIEVE(a,b)
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) (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 " s - Skip the playback of instructions for leaving a message to the\n"
471 " calling party.\n"
472 " u - Play the 'unavailable' greeting.\n"
473 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
474 " error occurs.\n";
476 static char *synopsis_vmain =
477 "Check Voicemail messages";
479 static char *descrip_vmain =
480 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
481 "calling party to check voicemail messages. A specific mailbox, and optional\n"
482 "corresponding context, may be specified. If a mailbox is not provided, the\n"
483 "calling party will be prompted to enter one. If a context is not specified,\n"
484 "the 'default' context will be used.\n\n"
485 " Options:\n"
486 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
487 " is entered by the caller.\n"
488 " g(#) - Use the specified amount of gain when recording a voicemail\n"
489 " message. The units are whole-number decibels (dB).\n"
490 " s - Skip checking the passcode for the mailbox.\n"
491 " a(#) - Skip folder prompt and go directly to folder specified.\n"
492 " Defaults to INBOX\n";
494 static char *synopsis_vm_box_exists =
495 "Check to see if Voicemail mailbox exists";
497 static char *descrip_vm_box_exists =
498 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
499 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
500 "will be used.\n"
501 " This application will set the following channel variable upon completion:\n"
502 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
503 " MailboxExists application. Possible values include:\n"
504 " SUCCESS | FAILED\n\n"
505 " Options:\n"
506 " j - Jump to priority n+101 if the mailbox is found.\n";
508 static char *synopsis_vmauthenticate =
509 "Authenticate with Voicemail passwords";
511 static char *descrip_vmauthenticate =
512 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
513 "same way as the Authenticate application, but the passwords are taken from\n"
514 "voicemail.conf.\n"
515 " If the mailbox is specified, only that mailbox's password will be considered\n"
516 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
517 "be set with the authenticated mailbox.\n\n"
518 " Options:\n"
519 " s - Skip playing the initial prompts.\n";
521 /* Leave a message */
522 static char *app = "VoiceMail";
524 /* Check mail, control, etc */
525 static char *app2 = "VoiceMailMain";
527 static char *app3 = "MailboxExists";
528 static char *app4 = "VMAuthenticate";
530 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
531 static AST_LIST_HEAD_STATIC(zones, vm_zone);
532 static int maxsilence;
533 static int maxmsg;
534 static int silencethreshold = 128;
535 static char serveremail[80];
536 static char mailcmd[160]; /* Configurable mail cmd */
537 static char externnotify[160];
538 static struct ast_smdi_interface *smdi_iface = NULL;
539 static char vmfmts[80];
540 static double volgain;
541 static int vmminmessage;
542 static int vmmaxmessage;
543 static int maxgreet;
544 static int skipms;
545 static int maxlogins;
547 static struct ast_flags globalflags = {0};
549 static int saydurationminfo;
551 static char dialcontext[AST_MAX_CONTEXT];
552 static char callcontext[AST_MAX_CONTEXT];
553 static char exitcontext[AST_MAX_CONTEXT];
555 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
558 static char *emailbody = NULL;
559 static char *emailsubject = NULL;
560 static char *pagerbody = NULL;
561 static char *pagersubject = NULL;
562 static char fromstring[100];
563 static char pagerfromstring[100];
564 static char emailtitle[100];
565 static char charset[32] = "ISO-8859-1";
567 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
568 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
569 static int adsiver = 1;
570 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
573 static void populate_defaults(struct ast_vm_user *vmu)
575 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
576 if (saydurationminfo)
577 vmu->saydurationm = saydurationminfo;
578 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
579 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
580 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
581 if (maxmsg)
582 vmu->maxmsg = maxmsg;
583 vmu->volgain = volgain;
586 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
588 int x;
589 if (!strcasecmp(var, "attach")) {
590 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
591 } else if (!strcasecmp(var, "attachfmt")) {
592 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
593 } else if (!strcasecmp(var, "serveremail")) {
594 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
595 } else if (!strcasecmp(var, "language")) {
596 ast_copy_string(vmu->language, value, sizeof(vmu->language));
597 } else if (!strcasecmp(var, "tz")) {
598 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
599 #ifdef IMAP_STORAGE
600 } else if (!strcasecmp(var, "imapuser")) {
601 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
602 } else if (!strcasecmp(var, "imappassword")) {
603 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
604 #endif
605 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
606 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
607 } else if (!strcasecmp(var, "saycid")){
608 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
609 } else if (!strcasecmp(var,"sendvoicemail")){
610 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
611 } else if (!strcasecmp(var, "review")){
612 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
613 } else if (!strcasecmp(var, "tempgreetwarn")){
614 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
615 } else if (!strcasecmp(var, "operator")){
616 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
617 } else if (!strcasecmp(var, "envelope")){
618 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
619 } else if (!strcasecmp(var, "sayduration")){
620 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
621 } else if (!strcasecmp(var, "saydurationm")){
622 if (sscanf(value, "%d", &x) == 1) {
623 vmu->saydurationm = x;
624 } else {
625 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
627 } else if (!strcasecmp(var, "forcename")){
628 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
629 } else if (!strcasecmp(var, "forcegreetings")){
630 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
631 } else if (!strcasecmp(var, "callback")) {
632 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
633 } else if (!strcasecmp(var, "dialout")) {
634 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
635 } else if (!strcasecmp(var, "exitcontext")) {
636 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
637 } else if (!strcasecmp(var, "maxmsg")) {
638 vmu->maxmsg = atoi(value);
639 if (vmu->maxmsg <= 0) {
640 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
641 vmu->maxmsg = MAXMSG;
642 } else if (vmu->maxmsg > MAXMSGLIMIT) {
643 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
644 vmu->maxmsg = MAXMSGLIMIT;
646 } else if (!strcasecmp(var, "volgain")) {
647 sscanf(value, "%lf", &vmu->volgain);
648 } else if (!strcasecmp(var, "options")) {
649 apply_options(vmu, value);
653 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
655 int res;
656 if (!ast_strlen_zero(vmu->uniqueid)) {
657 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
658 if (res > 0) {
659 ast_copy_string(vmu->password, password, sizeof(vmu->password));
660 res = 0;
661 } else if (!res) {
662 res = -1;
664 return res;
666 return -1;
669 static void apply_options(struct ast_vm_user *vmu, const char *options)
670 { /* Destructively Parse options and apply */
671 char *stringp;
672 char *s;
673 char *var, *value;
674 stringp = ast_strdupa(options);
675 while ((s = strsep(&stringp, "|"))) {
676 value = s;
677 if ((var = strsep(&value, "=")) && value) {
678 apply_option(vmu, var, value);
683 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
685 struct ast_variable *tmp;
686 tmp = var;
687 while (tmp) {
688 if (!strcasecmp(tmp->name, "vmsecret")) {
689 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
690 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
691 if (ast_strlen_zero(retval->password))
692 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
693 } else if (!strcasecmp(tmp->name, "uniqueid")) {
694 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
695 } else if (!strcasecmp(tmp->name, "pager")) {
696 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
697 } else if (!strcasecmp(tmp->name, "email")) {
698 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
699 } else if (!strcasecmp(tmp->name, "fullname")) {
700 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
701 } else if (!strcasecmp(tmp->name, "context")) {
702 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
703 #ifdef IMAP_STORAGE
704 } else if (!strcasecmp(tmp->name, "imapuser")) {
705 ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
706 } else if (!strcasecmp(tmp->name, "imappassword")) {
707 ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
708 #endif
709 } else
710 apply_option(retval, tmp->name, tmp->value);
711 tmp = tmp->next;
715 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
717 struct ast_variable *var;
718 struct ast_vm_user *retval;
720 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
721 if (!ivm)
722 ast_set_flag(retval, VM_ALLOCED);
723 else
724 memset(retval, 0, sizeof(*retval));
725 if (mailbox)
726 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
727 populate_defaults(retval);
728 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
729 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
730 else
731 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
732 if (var) {
733 apply_options_full(retval, var);
734 ast_variables_destroy(var);
735 } else {
736 if (!ivm)
737 free(retval);
738 retval = NULL;
741 return retval;
744 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
746 /* This function could be made to generate one from a database, too */
747 struct ast_vm_user *vmu=NULL, *cur;
748 AST_LIST_LOCK(&users);
750 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
751 context = "default";
753 AST_LIST_TRAVERSE(&users, cur, list) {
754 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
755 break;
756 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
757 break;
759 if (cur) {
760 /* Make a copy, so that on a reload, we have no race */
761 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
762 memcpy(vmu, cur, sizeof(*vmu));
763 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
764 AST_LIST_NEXT(vmu, list) = NULL;
766 } else
767 vmu = find_user_realtime(ivm, context, mailbox);
768 AST_LIST_UNLOCK(&users);
769 return vmu;
772 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
774 /* This function could be made to generate one from a database, too */
775 struct ast_vm_user *cur;
776 int res = -1;
777 AST_LIST_LOCK(&users);
778 AST_LIST_TRAVERSE(&users, cur, list) {
779 if ((!context || !strcasecmp(context, cur->context)) &&
780 (!strcasecmp(mailbox, cur->mailbox)))
781 break;
783 if (cur) {
784 ast_copy_string(cur->password, newpass, sizeof(cur->password));
785 res = 0;
787 AST_LIST_UNLOCK(&users);
788 return res;
791 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
793 struct ast_config *cfg=NULL;
794 struct ast_variable *var=NULL;
795 struct ast_category *cat=NULL;
796 char *category=NULL, *value=NULL, *new=NULL;
797 const char *tmp=NULL;
799 if (!change_password_realtime(vmu, newpassword))
800 return;
802 /* check voicemail.conf */
803 if ((cfg = ast_config_load_with_comments(VOICEMAIL_CONFIG))) {
804 while ((category = ast_category_browse(cfg, category))) {
805 if (!strcasecmp(category, vmu->context)) {
806 tmp = ast_variable_retrieve(cfg, category, vmu->mailbox);
807 if (!tmp) {
808 ast_log(LOG_WARNING, "We could not find the mailbox.\n");
809 break;
811 value = strstr(tmp,",");
812 if (!value) {
813 ast_log(LOG_WARNING, "variable has bad format.\n");
814 break;
816 new = alloca((strlen(value)+strlen(newpassword)+1));
817 sprintf(new,"%s%s", newpassword, value);
818 if (!(cat = ast_category_get(cfg, category))) {
819 ast_log(LOG_WARNING, "Failed to get category structure.\n");
820 break;
822 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
825 /* save the results */
826 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
827 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
828 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
830 category = NULL;
831 var = NULL;
832 /* check users.conf and update the password stored for the mailbox*/
833 /* if no vmsecret entry exists create one. */
834 if ((cfg = ast_config_load_with_comments("users.conf"))) {
835 if (option_debug > 3)
836 ast_log(LOG_DEBUG, "we are looking for %s\n", vmu->mailbox);
837 while ((category = ast_category_browse(cfg, category))) {
838 if (option_debug > 3)
839 ast_log(LOG_DEBUG, "users.conf: %s\n", category);
840 if (!strcasecmp(category, vmu->mailbox)) {
841 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
842 if (option_debug > 3)
843 ast_log(LOG_DEBUG, "looks like we need to make vmsecret!\n");
844 var = ast_variable_new("vmsecret", newpassword);
846 new = alloca(strlen(newpassword)+1);
847 sprintf(new, "%s", newpassword);
848 if (!(cat = ast_category_get(cfg, category))) {
849 if (option_debug > 3)
850 ast_log(LOG_DEBUG, "failed to get category!\n");
851 break;
853 if (!var)
854 ast_variable_update(cat, "vmsecret", new, NULL, 0);
855 else
856 ast_variable_append(cat, var);
859 /* save the results and clean things up */
860 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
861 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
862 config_text_file_save("users.conf", cfg, "AppVoicemail");
866 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
868 char buf[255];
869 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
870 if (!ast_safe_system(buf)) {
871 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
872 /* Reset the password in memory, too */
873 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
877 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
879 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
882 #ifdef IMAP_STORAGE
883 static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num)
885 if (mkdir(dir, 01777) && (errno != EEXIST)) {
886 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
887 return snprintf(dest, len, "%s/msg%04d", dir, num);
889 return snprintf(dest, len, "%s/msg%04d", dir, num);
892 static void vm_imap_delete(int msgnum, struct vm_state *vms)
894 unsigned long messageNum = 0;
895 char arg[10];
897 /* find real message number based on msgnum */
898 /* this may be an index into vms->msgArray based on the msgnum. */
900 messageNum = vms->msgArray[msgnum];
901 if (messageNum == 0) {
902 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
903 return;
905 if (option_debug > 2)
906 ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
907 /* delete message */
908 snprintf (arg, sizeof(arg), "%lu",messageNum);
909 mail_setflag (vms->mailstream,arg,"\\DELETED");
912 #endif
913 static int make_file(char *dest, int len, char *dir, int num)
915 return snprintf(dest, len, "%s/msg%04d", dir, num);
918 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
919 * \param dest String. base directory.
920 * \param len Length of dest.
921 * \param context String. Ignored if is null or empty string.
922 * \param ext String. Ignored if is null or empty string.
923 * \param folder String. Ignored if is null or empty string.
924 * \return -1 on failure, 0 on success.
926 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
928 mode_t mode = VOICEMAIL_DIR_MODE;
930 if (!ast_strlen_zero(context)) {
931 make_dir(dest, len, context, "", "");
932 if (mkdir(dest, mode) && errno != EEXIST) {
933 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
934 return -1;
937 if (!ast_strlen_zero(ext)) {
938 make_dir(dest, len, context, ext, "");
939 if (mkdir(dest, mode) && errno != EEXIST) {
940 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
941 return -1;
944 if (!ast_strlen_zero(folder)) {
945 make_dir(dest, len, context, ext, folder);
946 if (mkdir(dest, mode) && errno != EEXIST) {
947 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
948 return -1;
951 return 0;
954 /* only return failure if ast_lock_path returns 'timeout',
955 not if the path does not exist or any other reason
957 static int vm_lock_path(const char *path)
959 switch (ast_lock_path(path)) {
960 case AST_LOCK_TIMEOUT:
961 return -1;
962 default:
963 return 0;
968 #ifdef ODBC_STORAGE
969 struct generic_prepare_struct {
970 char *sql;
971 int argc;
972 char **argv;
975 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
977 struct generic_prepare_struct *gps = data;
978 int res, i;
979 SQLHSTMT stmt;
981 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
982 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
983 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
984 return NULL;
986 res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
987 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
988 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
989 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
990 return NULL;
992 for (i = 0; i < gps->argc; i++)
993 SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
995 return stmt;
998 static int retrieve_file(char *dir, int msgnum)
1000 int x = 0;
1001 int res;
1002 int fd=-1;
1003 size_t fdlen = 0;
1004 void *fdm = MAP_FAILED;
1005 SQLSMALLINT colcount=0;
1006 SQLHSTMT stmt;
1007 char sql[PATH_MAX];
1008 char fmt[80]="";
1009 char *c;
1010 char coltitle[256];
1011 SQLSMALLINT collen;
1012 SQLSMALLINT datatype;
1013 SQLSMALLINT decimaldigits;
1014 SQLSMALLINT nullable;
1015 SQLULEN colsize;
1016 SQLLEN colsize2;
1017 FILE *f=NULL;
1018 char rowdata[80];
1019 char fn[PATH_MAX];
1020 char full_fn[PATH_MAX];
1021 char msgnums[80];
1022 char *argv[] = { dir, msgnums };
1023 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1025 struct odbc_obj *obj;
1026 obj = ast_odbc_request_obj(odbc_database, 0);
1027 if (obj) {
1028 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1029 c = strchr(fmt, '|');
1030 if (c)
1031 *c = '\0';
1032 if (!strcasecmp(fmt, "wav49"))
1033 strcpy(fmt, "WAV");
1034 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1035 if (msgnum > -1)
1036 make_file(fn, sizeof(fn), dir, msgnum);
1037 else
1038 ast_copy_string(fn, dir, sizeof(fn));
1039 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1041 if (!(f = fopen(full_fn, "w+"))) {
1042 ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
1043 goto yuck;
1046 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1047 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1048 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1049 if (!stmt) {
1050 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1051 ast_odbc_release_obj(obj);
1052 goto yuck;
1054 res = SQLFetch(stmt);
1055 if (res == SQL_NO_DATA) {
1056 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1057 ast_odbc_release_obj(obj);
1058 goto yuck;
1060 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1061 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1062 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1063 ast_odbc_release_obj(obj);
1064 goto yuck;
1066 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, 0770);
1067 if (fd < 0) {
1068 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1069 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1070 ast_odbc_release_obj(obj);
1071 goto yuck;
1073 res = SQLNumResultCols(stmt, &colcount);
1074 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1075 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1076 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1077 ast_odbc_release_obj(obj);
1078 goto yuck;
1080 if (f)
1081 fprintf(f, "[message]\n");
1082 for (x=0;x<colcount;x++) {
1083 rowdata[0] = '\0';
1084 collen = sizeof(coltitle);
1085 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
1086 &datatype, &colsize, &decimaldigits, &nullable);
1087 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1088 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1089 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1090 ast_odbc_release_obj(obj);
1091 goto yuck;
1093 if (!strcasecmp(coltitle, "recording")) {
1094 off_t offset;
1095 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
1096 fdlen = colsize2;
1097 if (fd > -1) {
1098 char tmp[1]="";
1099 lseek(fd, fdlen - 1, SEEK_SET);
1100 if (write(fd, tmp, 1) != 1) {
1101 close(fd);
1102 fd = -1;
1103 continue;
1105 /* Read out in small chunks */
1106 for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1107 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
1108 ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1109 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1110 ast_odbc_release_obj(obj);
1111 goto yuck;
1112 } else {
1113 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
1114 munmap(fdm, CHUNKSIZE);
1115 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1116 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1117 unlink(full_fn);
1118 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1119 ast_odbc_release_obj(obj);
1120 goto yuck;
1124 truncate(full_fn, fdlen);
1126 } else {
1127 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1128 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1129 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1130 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1131 ast_odbc_release_obj(obj);
1132 goto yuck;
1134 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1135 fprintf(f, "%s=%s\n", coltitle, rowdata);
1138 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1139 ast_odbc_release_obj(obj);
1140 } else
1141 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1142 yuck:
1143 if (f)
1144 fclose(f);
1145 if (fd > -1)
1146 close(fd);
1147 return x - 1;
1150 static int remove_file(char *dir, int msgnum)
1152 char fn[PATH_MAX];
1153 char full_fn[PATH_MAX];
1154 char msgnums[80];
1156 if (msgnum > -1) {
1157 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1158 make_file(fn, sizeof(fn), dir, msgnum);
1159 } else
1160 ast_copy_string(fn, dir, sizeof(fn));
1161 ast_filedelete(fn, NULL);
1162 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1163 unlink(full_fn);
1164 return 0;
1167 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1169 int x = 0;
1170 int res;
1171 SQLHSTMT stmt;
1172 char sql[PATH_MAX];
1173 char rowdata[20];
1174 char *argv[] = { dir };
1175 struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
1177 struct odbc_obj *obj;
1178 obj = ast_odbc_request_obj(odbc_database, 0);
1179 if (obj) {
1180 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1181 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1182 if (!stmt) {
1183 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1184 ast_odbc_release_obj(obj);
1185 goto yuck;
1187 res = SQLFetch(stmt);
1188 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1189 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1190 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1191 ast_odbc_release_obj(obj);
1192 goto yuck;
1194 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1195 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1196 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1197 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1198 ast_odbc_release_obj(obj);
1199 goto yuck;
1201 if (sscanf(rowdata, "%d", &x) != 1)
1202 ast_log(LOG_WARNING, "Failed to read message count!\n");
1203 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1204 ast_odbc_release_obj(obj);
1205 } else
1206 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1207 yuck:
1208 return x - 1;
1211 static int message_exists(char *dir, int msgnum)
1213 int x = 0;
1214 int res;
1215 SQLHSTMT stmt;
1216 char sql[PATH_MAX];
1217 char rowdata[20];
1218 char msgnums[20];
1219 char *argv[] = { dir, msgnums };
1220 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1222 struct odbc_obj *obj;
1223 obj = ast_odbc_request_obj(odbc_database, 0);
1224 if (obj) {
1225 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1226 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1227 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1228 if (!stmt) {
1229 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1230 ast_odbc_release_obj(obj);
1231 goto yuck;
1233 res = SQLFetch(stmt);
1234 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1235 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1236 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1237 ast_odbc_release_obj(obj);
1238 goto yuck;
1240 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1241 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1242 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1243 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1244 ast_odbc_release_obj(obj);
1245 goto yuck;
1247 if (sscanf(rowdata, "%d", &x) != 1)
1248 ast_log(LOG_WARNING, "Failed to read message count!\n");
1249 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1250 ast_odbc_release_obj(obj);
1251 } else
1252 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1253 yuck:
1254 return x;
1257 static int count_messages(struct ast_vm_user *vmu, char *dir)
1259 return last_message_index(vmu, dir) + 1;
1262 static void delete_file(char *sdir, int smsg)
1264 SQLHSTMT stmt;
1265 char sql[PATH_MAX];
1266 char msgnums[20];
1267 char *argv[] = { sdir, msgnums };
1268 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1270 struct odbc_obj *obj;
1271 obj = ast_odbc_request_obj(odbc_database, 0);
1272 if (obj) {
1273 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1274 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1275 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1276 if (!stmt)
1277 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1278 else
1279 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1280 ast_odbc_release_obj(obj);
1281 } else
1282 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1283 return;
1286 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1288 SQLHSTMT stmt;
1289 char sql[512];
1290 char msgnums[20];
1291 char msgnumd[20];
1292 struct odbc_obj *obj;
1293 char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
1294 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1296 delete_file(ddir, dmsg);
1297 obj = ast_odbc_request_obj(odbc_database, 0);
1298 if (obj) {
1299 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1300 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1301 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);
1302 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1303 if (!stmt)
1304 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1305 else
1306 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1307 ast_odbc_release_obj(obj);
1308 } else
1309 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1310 return;
1313 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1315 int x = 0;
1316 int res;
1317 int fd = -1;
1318 void *fdm = MAP_FAILED;
1319 size_t fdlen = -1;
1320 SQLHSTMT stmt;
1321 SQLLEN len;
1322 char sql[PATH_MAX];
1323 char msgnums[20];
1324 char fn[PATH_MAX];
1325 char full_fn[PATH_MAX];
1326 char fmt[80]="";
1327 char *c;
1328 const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1329 const char *category = "";
1330 struct ast_config *cfg=NULL;
1331 struct odbc_obj *obj;
1333 delete_file(dir, msgnum);
1334 obj = ast_odbc_request_obj(odbc_database, 0);
1335 if (obj) {
1336 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1337 c = strchr(fmt, '|');
1338 if (c)
1339 *c = '\0';
1340 if (!strcasecmp(fmt, "wav49"))
1341 strcpy(fmt, "WAV");
1342 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1343 if (msgnum > -1)
1344 make_file(fn, sizeof(fn), dir, msgnum);
1345 else
1346 ast_copy_string(fn, dir, sizeof(fn));
1347 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1348 cfg = ast_config_load(full_fn);
1349 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1350 fd = open(full_fn, O_RDWR);
1351 if (fd < 0) {
1352 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1353 ast_odbc_release_obj(obj);
1354 goto yuck;
1356 if (cfg) {
1357 context = ast_variable_retrieve(cfg, "message", "context");
1358 if (!context) context = "";
1359 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1360 if (!macrocontext) macrocontext = "";
1361 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1362 if (!callerid) callerid = "";
1363 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1364 if (!origtime) origtime = "";
1365 duration = ast_variable_retrieve(cfg, "message", "duration");
1366 if (!duration) duration = "";
1367 category = ast_variable_retrieve(cfg, "message", "category");
1368 if (!category) category = "";
1370 fdlen = lseek(fd, 0, SEEK_END);
1371 lseek(fd, 0, SEEK_SET);
1372 printf("Length is %zd\n", fdlen);
1373 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1374 if (fdm == MAP_FAILED) {
1375 ast_log(LOG_WARNING, "Memory map failed!\n");
1376 ast_odbc_release_obj(obj);
1377 goto yuck;
1379 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1380 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1381 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1382 ast_odbc_release_obj(obj);
1383 goto yuck;
1385 if (!ast_strlen_zero(category))
1386 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1387 else
1388 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1389 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1390 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1391 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1392 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1393 ast_odbc_release_obj(obj);
1394 goto yuck;
1396 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1397 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1398 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1399 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1400 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1401 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1402 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1403 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1404 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1405 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1406 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1407 if (!ast_strlen_zero(category))
1408 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1409 res = ast_odbc_smart_execute(obj, stmt);
1410 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1411 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1412 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1413 ast_odbc_release_obj(obj);
1414 goto yuck;
1416 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1417 ast_odbc_release_obj(obj);
1418 } else
1419 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1420 yuck:
1421 if (cfg)
1422 ast_config_destroy(cfg);
1423 if (fdm != MAP_FAILED)
1424 munmap(fdm, fdlen);
1425 if (fd > -1)
1426 close(fd);
1427 return x;
1430 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1432 SQLHSTMT stmt;
1433 char sql[PATH_MAX];
1434 char msgnums[20];
1435 char msgnumd[20];
1436 struct odbc_obj *obj;
1437 char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
1438 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1440 delete_file(ddir, dmsg);
1441 obj = ast_odbc_request_obj(odbc_database, 0);
1442 if (obj) {
1443 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1444 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1445 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1446 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1447 if (!stmt)
1448 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1449 else
1450 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1451 ast_odbc_release_obj(obj);
1452 } else
1453 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1454 return;
1457 #else
1458 #ifndef IMAP_STORAGE
1459 static int count_messages(struct ast_vm_user *vmu, char *dir)
1461 /* Find all .txt files - even if they are not in sequence from 0000 */
1463 int vmcount = 0;
1464 DIR *vmdir = NULL;
1465 struct dirent *vment = NULL;
1467 if (vm_lock_path(dir))
1468 return ERROR_LOCK_PATH;
1470 if ((vmdir = opendir(dir))) {
1471 while ((vment = readdir(vmdir))) {
1472 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1473 vmcount++;
1475 closedir(vmdir);
1477 ast_unlock_path(dir);
1479 return vmcount;
1482 static void rename_file(char *sfn, char *dfn)
1484 char stxt[PATH_MAX];
1485 char dtxt[PATH_MAX];
1486 ast_filerename(sfn,dfn,NULL);
1487 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1488 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1489 rename(stxt, dtxt);
1491 #endif
1494 * A negative return value indicates an error.
1496 #if (!defined(IMAP_STORAGE) && !defined(ODBC_STORAGE))
1497 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1499 int x;
1500 char fn[PATH_MAX];
1502 if (vm_lock_path(dir))
1503 return ERROR_LOCK_PATH;
1505 for (x = 0; x < vmu->maxmsg; x++) {
1506 make_file(fn, sizeof(fn), dir, x);
1507 if (ast_fileexists(fn, NULL, NULL) < 1)
1508 break;
1510 ast_unlock_path(dir);
1512 return x - 1;
1514 #endif
1515 #endif
1517 static int copy(char *infile, char *outfile)
1519 int ifd;
1520 int ofd;
1521 int res;
1522 int len;
1523 char buf[4096];
1525 #ifdef HARDLINK_WHEN_POSSIBLE
1526 /* Hard link if possible; saves disk space & is faster */
1527 if (link(infile, outfile)) {
1528 #endif
1529 if ((ifd = open(infile, O_RDONLY)) < 0) {
1530 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1531 return -1;
1533 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1534 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1535 close(ifd);
1536 return -1;
1538 do {
1539 len = read(ifd, buf, sizeof(buf));
1540 if (len < 0) {
1541 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1542 close(ifd);
1543 close(ofd);
1544 unlink(outfile);
1546 if (len) {
1547 res = write(ofd, buf, len);
1548 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1549 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1550 close(ifd);
1551 close(ofd);
1552 unlink(outfile);
1555 } while (len);
1556 close(ifd);
1557 close(ofd);
1558 return 0;
1559 #ifdef HARDLINK_WHEN_POSSIBLE
1560 } else {
1561 /* Hard link succeeded */
1562 return 0;
1564 #endif
1567 static void copy_plain_file(char *frompath, char *topath)
1569 char frompath2[PATH_MAX], topath2[PATH_MAX];
1570 ast_filecopy(frompath, topath, NULL);
1571 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1572 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1573 copy(frompath2, topath2);
1576 static int vm_delete(char *file)
1578 char *txt;
1579 int txtsize = 0;
1581 txtsize = (strlen(file) + 5)*sizeof(char);
1582 txt = alloca(txtsize);
1583 /* Sprintf here would safe because we alloca'd exactly the right length,
1584 * but trying to eliminate all sprintf's anyhow
1586 snprintf(txt, txtsize, "%s.txt", file);
1587 unlink(txt);
1588 return ast_filedelete(file, NULL);
1591 static int inbuf(struct baseio *bio, FILE *fi)
1593 int l;
1595 if (bio->ateof)
1596 return 0;
1598 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1599 if (ferror(fi))
1600 return -1;
1602 bio->ateof = 1;
1603 return 0;
1606 bio->iolen= l;
1607 bio->iocp= 0;
1609 return 1;
1612 static int inchar(struct baseio *bio, FILE *fi)
1614 if (bio->iocp>=bio->iolen) {
1615 if (!inbuf(bio, fi))
1616 return EOF;
1619 return bio->iobuf[bio->iocp++];
1622 static int ochar(struct baseio *bio, int c, FILE *so)
1624 if (bio->linelength>=BASELINELEN) {
1625 if (fputs(eol,so)==EOF)
1626 return -1;
1628 bio->linelength= 0;
1631 if (putc(((unsigned char)c),so)==EOF)
1632 return -1;
1634 bio->linelength++;
1636 return 1;
1639 static int base_encode(char *filename, FILE *so)
1641 unsigned char dtable[BASEMAXINLINE];
1642 int i,hiteof= 0;
1643 FILE *fi;
1644 struct baseio bio;
1646 memset(&bio, 0, sizeof(bio));
1647 bio.iocp = BASEMAXINLINE;
1649 if (!(fi = fopen(filename, "rb"))) {
1650 ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
1651 return -1;
1654 for (i= 0;i<9;i++) {
1655 dtable[i]= 'A'+i;
1656 dtable[i+9]= 'J'+i;
1657 dtable[26+i]= 'a'+i;
1658 dtable[26+i+9]= 'j'+i;
1660 for (i= 0;i<8;i++) {
1661 dtable[i+18]= 'S'+i;
1662 dtable[26+i+18]= 's'+i;
1664 for (i= 0;i<10;i++) {
1665 dtable[52+i]= '0'+i;
1667 dtable[62]= '+';
1668 dtable[63]= '/';
1670 while (!hiteof){
1671 unsigned char igroup[3],ogroup[4];
1672 int c,n;
1674 igroup[0]= igroup[1]= igroup[2]= 0;
1676 for (n= 0;n<3;n++) {
1677 if ((c = inchar(&bio, fi)) == EOF) {
1678 hiteof= 1;
1679 break;
1682 igroup[n]= (unsigned char)c;
1685 if (n> 0) {
1686 ogroup[0]= dtable[igroup[0]>>2];
1687 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1688 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1689 ogroup[3]= dtable[igroup[2]&0x3F];
1691 if (n<3) {
1692 ogroup[3]= '=';
1694 if (n<2)
1695 ogroup[2]= '=';
1698 for (i= 0;i<4;i++)
1699 ochar(&bio, ogroup[i], so);
1703 fclose(fi);
1705 if (fputs(eol,so)==EOF)
1706 return 0;
1708 return 1;
1711 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)
1713 char callerid[256];
1714 /* Prepare variables for substition in email body and subject */
1715 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1716 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1717 snprintf(passdata, passdatasize, "%d", msgnum);
1718 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1719 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1720 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1721 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1722 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1723 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1724 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1725 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1728 static char *quote(const char *from, char *to, size_t len)
1730 char *ptr = to;
1731 *ptr++ = '"';
1732 for (; ptr < to + len - 1; from++) {
1733 if (*from == '"')
1734 *ptr++ = '\\';
1735 else if (*from == '\0')
1736 break;
1737 *ptr++ = *from;
1739 if (ptr < to + len - 1)
1740 *ptr++ = '"';
1741 *ptr = '\0';
1742 return to;
1745 * fill in *tm for current time according to the proper timezone, if any.
1746 * Return tm so it can be used as a function argument.
1748 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1750 const struct vm_zone *z = NULL;
1751 time_t t = time(NULL);
1753 /* Does this user have a timezone specified? */
1754 if (!ast_strlen_zero(vmu->zonetag)) {
1755 /* Find the zone in the list */
1756 AST_LIST_LOCK(&zones);
1757 AST_LIST_TRAVERSE(&zones, z, list) {
1758 if (!strcmp(z->name, vmu->zonetag))
1759 break;
1761 AST_LIST_UNLOCK(&zones);
1763 ast_localtime(&t, tm, z ? z->timezone : NULL);
1764 return tm;
1767 /* same as mkstemp, but return a FILE * */
1768 static FILE *vm_mkftemp(char *template)
1770 FILE *p = NULL;
1771 int pfd = mkstemp(template);
1772 chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
1773 if (pfd > -1) {
1774 p = fdopen(pfd, "w+");
1775 if (!p) {
1776 close(pfd);
1777 pfd = -1;
1780 return p;
1783 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)
1785 char date[256];
1786 char host[MAXHOSTNAMELEN] = "";
1787 char who[256];
1788 char bound[256];
1789 char fname[256];
1790 char dur[256];
1791 char tmpcmd[256];
1792 struct tm tm;
1793 char *passdata2;
1794 size_t len_passdata;
1795 #ifdef IMAP_STORAGE
1796 #define ENDL "\r\n"
1797 #else
1798 #define ENDL "\n"
1799 #endif
1801 gethostname(host, sizeof(host) - 1);
1802 if (strchr(srcemail, '@'))
1803 ast_copy_string(who, srcemail, sizeof(who));
1804 else {
1805 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1807 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1808 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1809 fprintf(p, "Date: %s" ENDL, date);
1811 /* Set date format for voicemail mail */
1812 strftime(date, sizeof(date), emaildateformat, &tm);
1814 if (*fromstring) {
1815 struct ast_channel *ast;
1816 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1817 char *passdata;
1818 int vmlen = strlen(fromstring)*3 + 200;
1819 if ((passdata = alloca(vmlen))) {
1820 memset(passdata, 0, vmlen);
1821 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1822 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1823 len_passdata = strlen(passdata) * 2 + 3;
1824 passdata2 = alloca(len_passdata);
1825 fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
1826 } else
1827 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1828 ast_channel_free(ast);
1829 } else
1830 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1831 } else
1832 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
1833 len_passdata = strlen(vmu->fullname) * 2 + 3;
1834 passdata2 = alloca(len_passdata);
1835 fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
1836 if (emailsubject) {
1837 struct ast_channel *ast;
1838 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1839 char *passdata;
1840 int vmlen = strlen(emailsubject)*3 + 200;
1841 if ((passdata = alloca(vmlen))) {
1842 memset(passdata, 0, vmlen);
1843 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1844 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
1845 fprintf(p, "Subject: %s" ENDL, passdata);
1846 } else
1847 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1848 ast_channel_free(ast);
1849 } else
1850 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1851 } else if (*emailtitle) {
1852 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1853 fprintf(p, ENDL) ;
1854 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1855 fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
1856 else
1857 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
1858 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
1859 if (imap) {
1860 /* additional information needed for IMAP searching */
1861 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
1862 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
1863 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
1864 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
1865 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
1866 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
1867 fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
1868 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
1869 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
1870 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
1871 if (!ast_strlen_zero(category))
1872 fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
1873 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
1874 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
1876 if (!ast_strlen_zero(cidnum))
1877 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, cidnum);
1878 if (!ast_strlen_zero(cidname))
1879 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, cidname);
1880 fprintf(p, "MIME-Version: 1.0" ENDL);
1881 if (attach_user_voicemail) {
1882 /* Something unique. */
1883 snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
1885 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
1886 fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
1887 fprintf(p, "--%s" ENDL, bound);
1889 fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
1890 if (emailbody) {
1891 struct ast_channel *ast;
1892 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1893 char *passdata;
1894 int vmlen = strlen(emailbody)*3 + 200;
1895 if ((passdata = alloca(vmlen))) {
1896 memset(passdata, 0, vmlen);
1897 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1898 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
1899 fprintf(p, "%s" ENDL, passdata);
1900 } else
1901 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1902 ast_channel_free(ast);
1903 } else
1904 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1905 } else {
1906 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
1908 "in mailbox %s from %s, on %s so you might" ENDL
1909 "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname,
1910 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1912 if (attach_user_voicemail) {
1913 /* Eww. We want formats to tell us their own MIME type */
1914 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
1915 char tmpdir[256], newtmp[256];
1916 int tmpfd = -1;
1918 if (vmu->volgain < -.001 || vmu->volgain > .001) {
1919 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
1920 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
1921 tmpfd = mkstemp(newtmp);
1922 chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
1923 if (option_debug > 2)
1924 ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
1925 if (tmpfd > -1) {
1926 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
1927 ast_safe_system(tmpcmd);
1928 attach = newtmp;
1929 if (option_debug > 2)
1930 ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
1933 fprintf(p, "--%s" ENDL, bound);
1934 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
1935 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
1936 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
1937 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
1938 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1939 base_encode(fname, p);
1940 fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
1941 if (tmpfd > -1) {
1942 unlink(fname);
1943 close(tmpfd);
1944 unlink(newtmp);
1947 #undef ENDL
1949 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)
1951 FILE *p=NULL;
1952 char tmp[80] = "/tmp/astmail-XXXXXX";
1953 char tmp2[256];
1955 if (vmu && ast_strlen_zero(vmu->email)) {
1956 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
1957 return(0);
1959 if (!strcmp(format, "wav49"))
1960 format = "WAV";
1961 if (option_debug > 2)
1962 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));
1963 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1964 command hangs */
1965 if ((p = vm_mkftemp(tmp)) == NULL) {
1966 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1967 return -1;
1968 } else {
1969 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
1970 fclose(p);
1971 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1972 ast_safe_system(tmp2);
1973 if (option_debug > 2)
1974 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
1976 return 0;
1979 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)
1981 char date[256];
1982 char host[MAXHOSTNAMELEN] = "";
1983 char who[256];
1984 char dur[PATH_MAX];
1985 char tmp[80] = "/tmp/astmail-XXXXXX";
1986 char tmp2[PATH_MAX];
1987 struct tm tm;
1988 FILE *p;
1990 if ((p = vm_mkftemp(tmp)) == NULL) {
1991 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1992 return -1;
1993 } else {
1994 gethostname(host, sizeof(host)-1);
1995 if (strchr(srcemail, '@'))
1996 ast_copy_string(who, srcemail, sizeof(who));
1997 else {
1998 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2000 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2001 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2002 fprintf(p, "Date: %s\n", date);
2004 if (*pagerfromstring) {
2005 struct ast_channel *ast;
2006 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2007 char *passdata;
2008 int vmlen = strlen(fromstring)*3 + 200;
2009 if ((passdata = alloca(vmlen))) {
2010 memset(passdata, 0, vmlen);
2011 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2012 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2013 fprintf(p, "From: %s <%s>\n", passdata, who);
2014 } else
2015 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2016 ast_channel_free(ast);
2017 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2018 } else
2019 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2020 fprintf(p, "To: %s\n", pager);
2021 if (pagersubject) {
2022 struct ast_channel *ast;
2023 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2024 char *passdata;
2025 int vmlen = strlen(pagersubject) * 3 + 200;
2026 if ((passdata = alloca(vmlen))) {
2027 memset(passdata, 0, vmlen);
2028 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2029 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2030 fprintf(p, "Subject: %s\n\n", passdata);
2031 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2032 ast_channel_free(ast);
2033 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2034 } else
2035 fprintf(p, "Subject: New VM\n\n");
2036 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2037 if (pagerbody) {
2038 struct ast_channel *ast;
2039 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2040 char *passdata;
2041 int vmlen = strlen(pagerbody)*3 + 200;
2042 if ((passdata = alloca(vmlen))) {
2043 memset(passdata, 0, vmlen);
2044 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2045 pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2046 fprintf(p, "%s\n", passdata);
2047 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2048 ast_channel_free(ast);
2049 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2050 } else {
2051 fprintf(p, "New %s long msg in box %s\n"
2052 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2054 fclose(p);
2055 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2056 ast_safe_system(tmp2);
2057 if (option_debug > 2)
2058 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
2060 return 0;
2063 static int get_date(char *s, int len)
2065 struct tm tm;
2066 time_t t;
2068 time(&t);
2070 ast_localtime(&t, &tm, NULL);
2072 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2075 static int play_greeting(struct ast_channel *chan, struct ast_vm_user *vmu, char *filename, char *ecodes)
2077 int res = -2;
2079 #ifdef ODBC_STORAGE
2080 int success =
2081 #endif
2082 RETRIEVE(filename, -1);
2083 if (ast_fileexists(filename, NULL, NULL) > 0) {
2084 res = ast_streamfile(chan, filename, chan->language);
2085 if (res > -1)
2086 res = ast_waitstream(chan, ecodes);
2087 #ifdef ODBC_STORAGE
2088 if (success == -1) {
2089 /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
2090 if (option_debug)
2091 ast_log(LOG_DEBUG, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
2092 store_file(filename, vmu->mailbox, vmu->context, -1);
2094 #endif
2096 DISPOSE(filename, -1);
2098 return res;
2101 static int invent_message(struct ast_channel *chan, struct ast_vm_user *vmu, char *ext, int busy, char *ecodes)
2103 int res;
2104 char fn[PATH_MAX];
2105 char dest[PATH_MAX];
2107 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, ext);
2109 if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "greet"))) {
2110 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
2111 return -1;
2114 res = play_greeting(chan, vmu, fn, ecodes);
2115 if (res == -2) {
2116 /* File did not exist */
2117 res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
2118 if (res)
2119 return res;
2120 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2123 if (res)
2124 return res;
2126 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
2127 return res;
2130 static void free_user(struct ast_vm_user *vmu)
2132 if (ast_test_flag(vmu, VM_ALLOCED))
2133 free(vmu);
2136 static void free_zone(struct vm_zone *z)
2138 free(z);
2141 static const char *mbox(int id)
2143 static const char *msgs[] = {
2144 "INBOX",
2145 "Old",
2146 "Work",
2147 "Family",
2148 "Friends",
2149 "Cust1",
2150 "Cust2",
2151 "Cust3",
2152 "Cust4",
2153 "Cust5",
2155 return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "tmp";
2157 #ifdef IMAP_STORAGE
2158 static int folder_int(const char *folder)
2160 /*assume a NULL folder means INBOX*/
2161 if (!folder)
2162 return 0;
2163 if (!strcasecmp(folder, "INBOX"))
2164 return 0;
2165 else if (!strcasecmp(folder, "Old"))
2166 return 1;
2167 else if (!strcasecmp(folder, "Work"))
2168 return 2;
2169 else if (!strcasecmp(folder, "Family"))
2170 return 3;
2171 else if (!strcasecmp(folder, "Friends"))
2172 return 4;
2173 else if (!strcasecmp(folder, "Cust1"))
2174 return 5;
2175 else if (!strcasecmp(folder, "Cust2"))
2176 return 6;
2177 else if (!strcasecmp(folder, "Cust3"))
2178 return 7;
2179 else if (!strcasecmp(folder, "Cust4"))
2180 return 8;
2181 else if (!strcasecmp(folder, "Cust5"))
2182 return 9;
2183 else /*assume they meant INBOX if folder is not found otherwise*/
2184 return 0;
2186 #endif
2188 #ifdef ODBC_STORAGE
2189 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
2190 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2192 int x = -1;
2193 int res;
2194 SQLHSTMT stmt;
2195 char sql[PATH_MAX];
2196 char rowdata[20];
2197 char tmp[PATH_MAX] = "";
2198 struct odbc_obj *obj;
2199 char *context;
2200 struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2202 if (newmsgs)
2203 *newmsgs = 0;
2204 if (oldmsgs)
2205 *oldmsgs = 0;
2207 /* If no mailbox, return immediately */
2208 if (ast_strlen_zero(mailbox))
2209 return 0;
2211 ast_copy_string(tmp, mailbox, sizeof(tmp));
2213 context = strchr(tmp, '@');
2214 if (context) {
2215 *context = '\0';
2216 context++;
2217 } else
2218 context = "default";
2220 obj = ast_odbc_request_obj(odbc_database, 0);
2221 if (obj) {
2222 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2223 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2224 if (!stmt) {
2225 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2226 ast_odbc_release_obj(obj);
2227 goto yuck;
2229 res = SQLFetch(stmt);
2230 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2231 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2232 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2233 ast_odbc_release_obj(obj);
2234 goto yuck;
2236 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2237 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2238 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2239 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2240 ast_odbc_release_obj(obj);
2241 goto yuck;
2243 *newmsgs = atoi(rowdata);
2244 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2246 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2247 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2248 if (!stmt) {
2249 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2250 ast_odbc_release_obj(obj);
2251 goto yuck;
2253 res = SQLFetch(stmt);
2254 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2255 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2256 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2257 ast_odbc_release_obj(obj);
2258 goto yuck;
2260 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2261 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2262 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2263 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2264 ast_odbc_release_obj(obj);
2265 goto yuck;
2267 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2268 ast_odbc_release_obj(obj);
2269 *oldmsgs = atoi(rowdata);
2270 x = 0;
2271 } else
2272 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2274 yuck:
2275 return x;
2278 static int messagecount(const char *context, const char *mailbox, const char *folder)
2280 struct odbc_obj *obj = NULL;
2281 int nummsgs = 0;
2282 int res;
2283 SQLHSTMT stmt = NULL;
2284 char sql[PATH_MAX];
2285 char rowdata[20];
2286 struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2287 if (!folder)
2288 folder = "INBOX";
2289 /* If no mailbox, return immediately */
2290 if (ast_strlen_zero(mailbox))
2291 return 0;
2293 obj = ast_odbc_request_obj(odbc_database, 0);
2294 if (obj) {
2295 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2296 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2297 if (!stmt) {
2298 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2299 goto yuck;
2301 res = SQLFetch(stmt);
2302 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2303 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2304 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2305 goto yuck;
2307 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2308 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2309 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2310 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2311 goto yuck;
2313 nummsgs = atoi(rowdata);
2314 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2315 } else
2316 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2318 yuck:
2319 if (obj)
2320 ast_odbc_release_obj(obj);
2321 return nummsgs;
2324 static int has_voicemail(const char *mailbox, const char *folder)
2326 char tmp[256], *tmp2 = tmp, *mbox, *context;
2327 ast_copy_string(tmp, mailbox, sizeof(tmp));
2328 while ((context = mbox = strsep(&tmp2, ","))) {
2329 strsep(&context, "@");
2330 if (ast_strlen_zero(context))
2331 context = "default";
2332 if (messagecount(context, mbox, folder))
2333 return 1;
2335 return 0;
2338 #elif defined(IMAP_STORAGE)
2340 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)
2342 char *myserveremail = serveremail;
2343 char fn[PATH_MAX];
2344 char mailbox[256];
2345 char *stringp;
2346 FILE *p=NULL;
2347 char tmp[80] = "/tmp/astmail-XXXXXX";
2348 long len;
2349 void *buf;
2350 int tempcopy = 0;
2351 STRING str;
2353 /*Greetings are not retrieved from IMAP, so there is no reason to attempt storing them there either*/
2354 if (msgnum < 0)
2355 return 0;
2357 /* Attach only the first format */
2358 fmt = ast_strdupa(fmt);
2359 stringp = fmt;
2360 strsep(&stringp, "|");
2362 if (!ast_strlen_zero(vmu->serveremail))
2363 myserveremail = vmu->serveremail;
2365 make_file(fn, sizeof(fn), dir, msgnum);
2367 if (ast_strlen_zero(vmu->email)) {
2368 /*we need the vmu->email to be set when we call make_email_file, but if we keep it set,
2369 * a duplicate e-mail will be created. So at the end of this function, we will revert back to an empty
2370 * string if tempcopy is 1
2372 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2373 tempcopy = 1;
2376 if (!strcmp(fmt, "wav49"))
2377 fmt = "WAV";
2378 if (option_debug > 2)
2379 ast_log(LOG_DEBUG, "Storing file '%s', format '%s'\n", fn, fmt);
2380 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2381 command hangs */
2382 if ((p = vm_mkftemp(tmp)) == NULL) {
2383 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2384 if (tempcopy)
2385 *(vmu->email) = '\0';
2386 return -1;
2387 } else {
2388 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);
2389 /* read mail file to memory */
2390 len = ftell(p);
2391 rewind(p);
2392 if ((buf = ast_malloc(len+1)) == NIL) {
2393 ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2394 fclose(p);
2395 return -1;
2397 fread(buf, len, 1, p);
2398 ((char *)buf)[len] = '\0';
2399 INIT(&str, mail_string, buf, len);
2400 init_mailstream(vms, 0);
2401 imap_mailbox_name(mailbox, sizeof(mailbox), vms, 0, 1);
2402 if (!mail_append(vms->mailstream, mailbox, &str))
2403 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2404 fclose(p);
2405 unlink(tmp);
2406 ast_free(buf);
2407 if (option_debug > 2)
2408 ast_log(LOG_DEBUG, "%s stored\n", fn);
2410 if (tempcopy)
2411 *(vmu->email) = '\0';
2412 return 0;
2416 static int messagecount(const char *context, const char *mailbox, const char *folder)
2418 SEARCHPGM *pgm;
2419 SEARCHHEADER *hdr;
2421 struct ast_vm_user *vmu, vmus;
2422 struct vm_state *vms_p;
2423 int ret = 0;
2424 int fold = folder_int(folder);
2426 if (ast_strlen_zero(mailbox))
2427 return 0;
2429 /* We have to get the user before we can open the stream! */
2430 /* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2431 vmu = find_user(&vmus, context, mailbox);
2432 if (!vmu) {
2433 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailbox,context);
2434 return -1;
2435 } else {
2436 /* No IMAP account available */
2437 if (vmu->imapuser[0] == '\0') {
2438 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2439 return -1;
2443 /* check if someone is accessing this box right now... */
2444 vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
2445 if (!vms_p) {
2446 vms_p = get_vm_state_by_mailbox(mailbox,1);
2448 if (vms_p) {
2449 if (option_debug > 2)
2450 ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
2451 if (fold == 0) {/*INBOX*/
2452 return vms_p->newmessages;
2454 if (fold == 1) {/*Old messages*/
2455 return vms_p->oldmessages;
2459 /* add one if not there... */
2460 vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
2461 if (!vms_p) {
2462 vms_p = get_vm_state_by_mailbox(mailbox,0);
2465 if (!vms_p) {
2466 if (option_debug > 2)
2467 ast_log (LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2468 if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
2469 return -1;
2471 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2472 ast_copy_string(vms_p->username, mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
2473 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2474 if (option_debug > 2)
2475 ast_log (LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2476 vms_p->updated = 1;
2477 /* set mailbox to INBOX! */
2478 ast_copy_string(vms_p->curbox, mbox(fold), sizeof(vms_p->curbox));
2479 init_vm_state(vms_p);
2480 vmstate_insert(vms_p);
2482 ret = init_mailstream(vms_p, fold);
2483 if (!vms_p->mailstream) {
2484 ast_log (LOG_ERROR,"IMAP mailstream is NULL\n");
2485 return -1;
2487 if (ret == 0) {
2488 pgm = mail_newsearchpgm ();
2489 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
2490 pgm->header = hdr;
2491 if (fold != 1) {
2492 pgm->unseen = 1;
2493 pgm->seen = 0;
2495 /* In the special case where fold is 1 (old messages) we have to do things a bit
2496 * differently. Old messages are stored in the INBOX but are marked as "seen"
2498 else {
2499 pgm->unseen = 0;
2500 pgm->seen = 1;
2502 pgm->undeleted = 1;
2503 pgm->deleted = 0;
2505 vms_p->vmArrayIndex = 0;
2506 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2507 if (fold == 0)
2508 vms_p->newmessages = vms_p->vmArrayIndex;
2509 if (fold == 1)
2510 vms_p->oldmessages = vms_p->vmArrayIndex;
2511 /*Freeing the searchpgm also frees the searchhdr*/
2512 mail_free_searchpgm(&pgm);
2513 vms_p->updated = 0;
2514 return vms_p->vmArrayIndex;
2515 } else {
2516 mail_ping(vms_p->mailstream);
2518 return 0;
2520 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
2522 char tmp[PATH_MAX] = "";
2523 char *mailboxnc;
2524 char *context;
2525 char *mb;
2526 char *cur;
2527 if (newmsgs)
2528 *newmsgs = 0;
2529 if (oldmsgs)
2530 *oldmsgs = 0;
2532 if (option_debug > 2)
2533 ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox_context);
2534 /* If no mailbox, return immediately */
2535 if (ast_strlen_zero(mailbox_context))
2536 return 0;
2538 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2539 context = strchr(tmp, '@');
2540 if (strchr(mailbox_context, ',')) {
2541 int tmpnew, tmpold;
2542 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2543 mb = tmp;
2544 while ((cur = strsep(&mb, ", "))) {
2545 if (!ast_strlen_zero(cur)) {
2546 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2547 return -1;
2548 else {
2549 if (newmsgs)
2550 *newmsgs += tmpnew;
2551 if (oldmsgs)
2552 *oldmsgs += tmpold;
2556 return 0;
2558 if (context) {
2559 *context = '\0';
2560 mailboxnc = tmp;
2561 context++;
2562 } else {
2563 context = "default";
2564 mailboxnc = (char *)mailbox_context;
2566 if (newmsgs) {
2567 if ((*newmsgs = messagecount(context, mailboxnc, "INBOX")) < 0)
2568 return -1;
2570 if (oldmsgs) {
2571 if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
2572 return -1;
2574 return 0;
2578 static int has_voicemail(const char *mailbox, const char *folder)
2580 char tmp[256], *tmp2, *mbox, *context;
2581 ast_copy_string(tmp, mailbox, sizeof(tmp));
2582 tmp2 = tmp;
2583 if (strchr(tmp2, ',')) {
2584 while ((mbox = strsep(&tmp2, ","))) {
2585 if (!ast_strlen_zero(mbox)) {
2586 if (has_voicemail(mbox, folder))
2587 return 1;
2591 if ((context= strchr(tmp, '@')))
2592 *context++ = '\0';
2593 else
2594 context = "default";
2595 return messagecount(context, tmp, folder) ? 1 : 0;
2598 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)
2600 struct vm_state *sendvms = NULL, *destvms = NULL;
2601 char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2602 if (msgnum >= recip->maxmsg) {
2603 ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
2604 return -1;
2606 if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
2607 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
2608 return -1;
2610 if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
2611 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
2612 return -1;
2614 snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
2615 if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T))
2616 return 0;
2617 ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
2618 return -1;
2621 #endif
2622 #ifndef IMAP_STORAGE
2623 /* copy message only used by file storage */
2624 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)
2626 char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
2627 const char *frombox = mbox(imbox);
2628 int recipmsgnum;
2630 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2632 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2634 if (!dir)
2635 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2636 else
2637 ast_copy_string(fromdir, dir, sizeof(fromdir));
2639 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2641 if (vm_lock_path(todir))
2642 return ERROR_LOCK_PATH;
2644 recipmsgnum = 0;
2645 do {
2646 make_file(topath, sizeof(topath), todir, recipmsgnum);
2647 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
2648 break;
2649 recipmsgnum++;
2650 } while (recipmsgnum < recip->maxmsg);
2651 if (recipmsgnum < recip->maxmsg) {
2652 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2653 } else {
2654 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2656 ast_unlock_path(todir);
2657 notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2659 return 0;
2661 #endif
2662 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2663 static int messagecount(const char *context, const char *mailbox, const char *folder)
2665 return __has_voicemail(context, mailbox, folder, 0);
2669 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2671 DIR *dir;
2672 struct dirent *de;
2673 char fn[256];
2674 int ret = 0;
2675 if (!folder)
2676 folder = "INBOX";
2677 /* If no mailbox, return immediately */
2678 if (ast_strlen_zero(mailbox))
2679 return 0;
2680 if (!context)
2681 context = "default";
2682 snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2683 dir = opendir(fn);
2684 if (!dir)
2685 return 0;
2686 while ((de = readdir(dir))) {
2687 if (!strncasecmp(de->d_name, "msg", 3)) {
2688 if (shortcircuit) {
2689 ret = 1;
2690 break;
2691 } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2692 ret++;
2695 closedir(dir);
2696 return ret;
2700 static int has_voicemail(const char *mailbox, const char *folder)
2702 char tmp[256], *tmp2 = tmp, *mbox, *context;
2703 ast_copy_string(tmp, mailbox, sizeof(tmp));
2704 while ((mbox = strsep(&tmp2, ","))) {
2705 if ((context = strchr(mbox, '@')))
2706 *context++ = '\0';
2707 else
2708 context = "default";
2709 if (__has_voicemail(context, mbox, folder, 1))
2710 return 1;
2712 return 0;
2716 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2718 char tmp[256];
2719 char *context;
2721 if (newmsgs)
2722 *newmsgs = 0;
2723 if (oldmsgs)
2724 *oldmsgs = 0;
2725 /* If no mailbox, return immediately */
2726 if (ast_strlen_zero(mailbox))
2727 return 0;
2728 if (strchr(mailbox, ',')) {
2729 int tmpnew, tmpold;
2730 char *mb, *cur;
2732 ast_copy_string(tmp, mailbox, sizeof(tmp));
2733 mb = tmp;
2734 while ((cur = strsep(&mb, ", "))) {
2735 if (!ast_strlen_zero(cur)) {
2736 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2737 return -1;
2738 else {
2739 if (newmsgs)
2740 *newmsgs += tmpnew;
2741 if (oldmsgs)
2742 *oldmsgs += tmpold;
2746 return 0;
2748 ast_copy_string(tmp, mailbox, sizeof(tmp));
2749 context = strchr(tmp, '@');
2750 if (context) {
2751 *context = '\0';
2752 context++;
2753 } else
2754 context = "default";
2755 if (newmsgs)
2756 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2757 if (oldmsgs)
2758 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2759 return 0;
2762 #endif
2764 static void run_externnotify(char *context, char *extension)
2766 char arguments[255];
2767 char ext_context[256] = "";
2768 int newvoicemails = 0, oldvoicemails = 0;
2769 struct ast_smdi_mwi_message *mwi_msg;
2771 if (!ast_strlen_zero(context))
2772 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2773 else
2774 ast_copy_string(ext_context, extension, sizeof(ext_context));
2776 if (!strcasecmp(externnotify, "smdi")) {
2777 if (ast_app_has_voicemail(ext_context, NULL))
2778 ast_smdi_mwi_set(smdi_iface, extension);
2779 else
2780 ast_smdi_mwi_unset(smdi_iface, extension);
2782 if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
2783 ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
2784 if (!strncmp(mwi_msg->cause, "INV", 3))
2785 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2786 else if (!strncmp(mwi_msg->cause, "BLK", 3))
2787 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2788 ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2789 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2790 } else {
2791 if (option_debug)
2792 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s\n", extension);
2794 } else if (!ast_strlen_zero(externnotify)) {
2795 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2796 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2797 } else {
2798 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2799 if (option_debug)
2800 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2801 ast_safe_system(arguments);
2806 struct leave_vm_options {
2807 unsigned int flags;
2808 signed char record_gain;
2811 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2813 #ifdef IMAP_STORAGE
2814 int newmsgs, oldmsgs;
2815 struct vm_state *vms = NULL;
2816 #endif
2817 char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
2818 char callerid[256];
2819 FILE *txt;
2820 char date[256];
2821 int txtdes;
2822 int res = 0;
2823 int msgnum;
2824 int duration = 0;
2825 int ausemacro = 0;
2826 int ousemacro = 0;
2827 int ouseexten = 0;
2828 char dir[PATH_MAX], tmpdir[PATH_MAX];
2829 char dest[PATH_MAX];
2830 char fn[PATH_MAX];
2831 char prefile[PATH_MAX] = "";
2832 char tempfile[PATH_MAX] = "";
2833 char ext_context[256] = "";
2834 char fmt[80];
2835 char *context;
2836 char ecodes[16] = "#";
2837 char tmp[1024] = "", *tmpptr;
2838 struct ast_vm_user *vmu;
2839 struct ast_vm_user svm;
2840 const char *category = NULL;
2842 ast_copy_string(tmp, ext, sizeof(tmp));
2843 ext = tmp;
2844 context = strchr(tmp, '@');
2845 if (context) {
2846 *context++ = '\0';
2847 tmpptr = strchr(context, '&');
2848 } else {
2849 tmpptr = strchr(ext, '&');
2852 if (tmpptr)
2853 *tmpptr++ = '\0';
2855 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2857 if (option_debug > 2)
2858 ast_log(LOG_DEBUG, "Before find_user\n");
2859 if (!(vmu = find_user(&svm, context, ext))) {
2860 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2861 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2862 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2863 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2864 return res;
2866 /* Setup pre-file if appropriate */
2867 if (strcmp(vmu->context, "default"))
2868 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2869 else
2870 ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
2871 if (ast_test_flag(options, OPT_BUSY_GREETING)) {
2872 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
2873 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2874 } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
2875 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
2876 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2878 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2879 if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
2880 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
2881 return -1;
2883 RETRIEVE(tempfile, -1);
2884 if (ast_fileexists(tempfile, NULL, NULL) > 0)
2885 ast_copy_string(prefile, tempfile, sizeof(prefile));
2886 DISPOSE(tempfile, -1);
2887 /* It's easier just to try to make it than to check for its existence */
2888 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2889 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
2891 /* Check current or macro-calling context for special extensions */
2892 if (ast_test_flag(vmu, VM_OPERATOR)) {
2893 if (!ast_strlen_zero(vmu->exit)) {
2894 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
2895 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2896 ouseexten = 1;
2898 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
2899 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2900 ouseexten = 1;
2902 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2903 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2904 ousemacro = 1;
2908 if (!ast_strlen_zero(vmu->exit)) {
2909 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2910 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2911 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2912 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2913 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2914 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2915 ausemacro = 1;
2918 /* Play the beginning intro if desired */
2919 if (!ast_strlen_zero(prefile)) {
2920 res = play_greeting(chan, vmu, prefile, ecodes);
2921 if (res == -2) {
2922 /* The file did not exist */
2923 if (option_debug)
2924 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2925 res = invent_message(chan, vmu, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2927 if (res < 0) {
2928 if (option_debug)
2929 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2930 free_user(vmu);
2931 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2932 return -1;
2935 if (res == '#') {
2936 /* On a '#' we skip the instructions */
2937 ast_set_flag(options, OPT_SILENT);
2938 res = 0;
2940 if (!res && !ast_test_flag(options, OPT_SILENT)) {
2941 res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
2942 if (res == '#') {
2943 ast_set_flag(options, OPT_SILENT);
2944 res = 0;
2947 if (res > 0)
2948 ast_stopstream(chan);
2949 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2950 other than the operator -- an automated attendant or mailbox login for example */
2951 if (res == '*') {
2952 chan->exten[0] = 'a';
2953 chan->exten[1] = '\0';
2954 if (!ast_strlen_zero(vmu->exit)) {
2955 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2956 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2957 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2959 chan->priority = 0;
2960 free_user(vmu);
2961 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2962 return 0;
2965 /* Check for a '0' here */
2966 if (res == '0') {
2967 transfer:
2968 if (ouseexten || ousemacro) {
2969 chan->exten[0] = 'o';
2970 chan->exten[1] = '\0';
2971 if (!ast_strlen_zero(vmu->exit)) {
2972 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2973 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2974 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2976 ast_play_and_wait(chan, "transfer");
2977 chan->priority = 0;
2978 free_user(vmu);
2979 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2981 return 0;
2983 if (res < 0) {
2984 free_user(vmu);
2985 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2986 return -1;
2988 /* The meat of recording the message... All the announcements and beeps have been played*/
2989 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2990 if (!ast_strlen_zero(fmt)) {
2991 msgnum = 0;
2993 #ifdef IMAP_STORAGE
2994 /* Is ext a mailbox? */
2995 /* must open stream for this user to get info! */
2996 res = inboxcount(ext_context, &newmsgs, &oldmsgs);
2997 if (res < 0) {
2998 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
2999 return -1;
3001 if (!(vms = get_vm_state_by_mailbox(ext,0))) {
3002 /*It is possible under certain circumstances that inboxcount did not create a vm_state when it was needed. This is a catchall which will
3003 * rarely be used*/
3004 if (!(vms = ast_calloc(1, sizeof(*vms)))) {
3005 ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
3006 return -1;
3008 ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
3009 ast_copy_string(vms->username, ext, sizeof(vms->username));
3010 vms->mailstream = NIL;
3011 if (option_debug > 2)
3012 ast_log(LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms->imapuser);
3013 vms->updated=1;
3014 ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
3015 init_vm_state(vms);
3016 vmstate_insert(vms);
3017 vms = get_vm_state_by_mailbox(ext,0);
3019 vms->newmessages++;
3020 /* here is a big difference! We add one to it later */
3021 msgnum = newmsgs + oldmsgs;
3022 if (option_debug > 2)
3023 ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3024 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
3025 /* set variable for compatability */
3026 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3028 /* Check if mailbox is full */
3029 check_quota(vms, imapfolder);
3030 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
3031 if (option_debug)
3032 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
3033 ast_play_and_wait(chan, "vm-mailboxfull");
3034 return -1;
3036 if (option_debug > 2)
3037 ast_log(LOG_DEBUG, "Checking message number quota - mailbox has %d messages, maximum is set to %d\n",msgnum,vmu->maxmsg);
3038 if (msgnum >= vmu->maxmsg) {
3039 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3040 if (!res)
3041 res = ast_waitstream(chan, "");
3042 ast_log(LOG_WARNING, "No more messages possible\n");
3043 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3044 goto leave_vm_out;
3047 /* Check if we have exceeded maxmsg */
3048 if (msgnum >= vmu->maxmsg) {
3049 ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u > %u)\n", msgnum, vmu->maxmsg);
3050 ast_play_and_wait(chan, "vm-mailboxfull");
3051 return -1;
3053 /* here is a big difference! We add one to it later */
3054 if (option_debug > 2)
3055 ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3056 #else
3057 if (count_messages(vmu, dir) >= vmu->maxmsg) {
3058 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3059 if (!res)
3060 res = ast_waitstream(chan, "");
3061 ast_log(LOG_WARNING, "No more messages possible\n");
3062 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3063 goto leave_vm_out;
3066 #endif
3067 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
3068 txtdes = mkstemp(tmptxtfile);
3069 chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
3070 if (txtdes < 0) {
3071 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3072 if (!res)
3073 res = ast_waitstream(chan, "");
3074 ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
3075 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3076 goto leave_vm_out;
3079 /* Now play the beep once we have the message number for our next message. */
3080 if (res >= 0) {
3081 /* Unless we're *really* silent, try to send the beep */
3082 res = ast_stream_and_wait(chan, "beep", chan->language, "");
3085 /* Store information */
3086 txt = fdopen(txtdes, "w+");
3087 if (txt) {
3088 get_date(date, sizeof(date));
3089 fprintf(txt,
3090 ";\n"
3091 "; Message Information file\n"
3092 ";\n"
3093 "[message]\n"
3094 "origmailbox=%s\n"
3095 "context=%s\n"
3096 "macrocontext=%s\n"
3097 "exten=%s\n"
3098 "priority=%d\n"
3099 "callerchan=%s\n"
3100 "callerid=%s\n"
3101 "origdate=%s\n"
3102 "origtime=%ld\n"
3103 "category=%s\n",
3104 ext,
3105 chan->context,
3106 chan->macrocontext,
3107 chan->exten,
3108 chan->priority,
3109 chan->name,
3110 ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
3111 date, (long)time(NULL),
3112 category ? category : "");
3113 } else
3114 ast_log(LOG_WARNING, "Error opening text file for output\n");
3115 #ifdef IMAP_STORAGE
3116 res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
3117 #else
3118 res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL);
3119 #endif
3121 if (txt) {
3122 if (duration < vmminmessage) {
3123 fclose(txt);
3124 if (option_verbose > 2)
3125 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
3126 ast_filedelete(tmptxtfile, NULL);
3127 unlink(tmptxtfile);
3128 } else {
3129 fprintf(txt, "duration=%d\n", duration);
3130 fclose(txt);
3131 if (vm_lock_path(dir)) {
3132 ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
3133 /* Delete files */
3134 ast_filedelete(tmptxtfile, NULL);
3135 unlink(tmptxtfile);
3136 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
3137 if (option_debug)
3138 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
3139 unlink(tmptxtfile);
3140 ast_unlock_path(dir);
3141 } else {
3142 for (;;) {
3143 make_file(fn, sizeof(fn), dir, msgnum);
3144 if (!EXISTS(dir, msgnum, fn, NULL))
3145 break;
3146 msgnum++;
3149 /* assign a variable with the name of the voicemail file */
3150 #ifndef IMAP_STORAGE
3151 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3152 #else
3153 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3154 #endif
3156 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3157 ast_filerename(tmptxtfile, fn, NULL);
3158 rename(tmptxtfile, txtfile);
3160 ast_unlock_path(dir);
3161 /* We must store the file first, before copying the message, because
3162 * ODBC storage does the entire copy with SQL.
3164 if (ast_fileexists(fn, NULL, NULL) > 0) {
3165 STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
3168 /* Are there to be more recipients of this message? */
3169 while (tmpptr) {
3170 struct ast_vm_user recipu, *recip;
3171 char *exten, *context;
3173 exten = strsep(&tmpptr, "&");
3174 context = strchr(exten, '@');
3175 if (context) {
3176 *context = '\0';
3177 context++;
3179 if ((recip = find_user(&recipu, context, exten))) {
3180 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
3181 free_user(recip);
3184 /* Notification and disposal needs to happen after the copy, though. */
3185 if (ast_fileexists(fn, NULL, NULL)) {
3186 notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
3187 DISPOSE(dir, msgnum);
3192 if (res == '0') {
3193 goto transfer;
3194 } else if (res > 0)
3195 res = 0;
3197 if (duration < vmminmessage)
3198 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3199 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3200 else
3201 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3202 } else
3203 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3204 leave_vm_out:
3205 free_user(vmu);
3207 return res;
3210 #ifndef IMAP_STORAGE
3211 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3213 /* we know max messages, so stop process when number is hit */
3215 int x,dest;
3216 char sfn[PATH_MAX];
3217 char dfn[PATH_MAX];
3219 if (vm_lock_path(dir))
3220 return ERROR_LOCK_PATH;
3222 for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3223 make_file(sfn, sizeof(sfn), dir, x);
3224 if (EXISTS(dir, x, sfn, NULL)) {
3226 if (x != dest) {
3227 make_file(dfn, sizeof(dfn), dir, dest);
3228 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3231 dest++;
3234 ast_unlock_path(dir);
3236 return 0;
3238 #endif
3240 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3242 int d;
3243 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
3244 return d;
3247 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3249 #ifdef IMAP_STORAGE
3250 /* we must use mbox(x) folder names, and copy the message there */
3251 /* simple. huh? */
3252 long res;
3253 char sequence[10];
3255 /* if save to Old folder, just leave in INBOX */
3256 if (box == 1) return 10;
3257 /* get the real IMAP message number for this message */
3258 snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
3259 if (option_debug > 2)
3260 ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,(char *) mbox(box));
3261 res = mail_copy(vms->mailstream,sequence,(char *) mbox(box));
3262 if (res == 1) return 0;
3263 return 1;
3264 #else
3265 char *dir = vms->curdir;
3266 char *username = vms->username;
3267 char *context = vmu->context;
3268 char sfn[PATH_MAX];
3269 char dfn[PATH_MAX];
3270 char ddir[PATH_MAX];
3271 const char *dbox = mbox(box);
3272 int x;
3273 make_file(sfn, sizeof(sfn), dir, msg);
3274 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
3276 if (vm_lock_path(ddir))
3277 return ERROR_LOCK_PATH;
3279 for (x = 0; x < vmu->maxmsg; x++) {
3280 make_file(dfn, sizeof(dfn), ddir, x);
3281 if (!EXISTS(ddir, x, dfn, NULL))
3282 break;
3284 if (x >= vmu->maxmsg) {
3285 ast_unlock_path(ddir);
3286 return ERROR_MAILBOX_FULL;
3288 if (strcmp(sfn, dfn)) {
3289 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
3291 ast_unlock_path(ddir);
3292 #endif
3293 return 0;
3296 static int adsi_logo(unsigned char *buf)
3298 int bytes = 0;
3299 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
3300 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
3301 return bytes;
3304 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
3306 unsigned char buf[256];
3307 int bytes=0;
3308 int x;
3309 char num[5];
3311 *useadsi = 0;
3312 bytes += ast_adsi_data_mode(buf + bytes);
3313 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3315 bytes = 0;
3316 bytes += adsi_logo(buf);
3317 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3318 #ifdef DISPLAY
3319 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
3320 #endif
3321 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3322 bytes += ast_adsi_data_mode(buf + bytes);
3323 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3325 if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
3326 bytes = 0;
3327 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
3328 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3329 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3330 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3331 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3332 return 0;
3335 #ifdef DISPLAY
3336 /* Add a dot */
3337 bytes = 0;
3338 bytes += ast_adsi_logo(buf);
3339 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3340 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
3341 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3342 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3343 #endif
3344 bytes = 0;
3345 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
3346 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
3347 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
3348 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
3349 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
3350 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
3351 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3353 #ifdef DISPLAY
3354 /* Add another dot */
3355 bytes = 0;
3356 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
3357 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3359 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3360 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3361 #endif
3363 bytes = 0;
3364 /* These buttons we load but don't use yet */
3365 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
3366 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
3367 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
3368 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
3369 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
3370 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
3371 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3373 #ifdef DISPLAY
3374 /* Add another dot */
3375 bytes = 0;
3376 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
3377 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3378 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3379 #endif
3381 bytes = 0;
3382 for (x=0;x<5;x++) {
3383 snprintf(num, sizeof(num), "%d", x);
3384 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
3386 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
3387 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3389 #ifdef DISPLAY
3390 /* Add another dot */
3391 bytes = 0;
3392 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
3393 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3394 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3395 #endif
3397 if (ast_adsi_end_download(chan)) {
3398 bytes = 0;
3399 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
3400 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3401 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3402 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3403 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3404 return 0;
3406 bytes = 0;
3407 bytes += ast_adsi_download_disconnect(buf + bytes);
3408 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3409 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3411 if (option_debug)
3412 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
3414 #ifdef DISPLAY
3415 /* Add last dot */
3416 bytes = 0;
3417 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
3418 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3419 #endif
3420 if (option_debug)
3421 ast_log(LOG_DEBUG, "Restarting session...\n");
3423 bytes = 0;
3424 /* Load the session now */
3425 if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
3426 *useadsi = 1;
3427 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
3428 } else
3429 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
3431 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3432 return 0;
3435 static void adsi_begin(struct ast_channel *chan, int *useadsi)
3437 int x;
3438 if (!ast_adsi_available(chan))
3439 return;
3440 x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
3441 if (x < 0)
3442 return;
3443 if (!x) {
3444 if (adsi_load_vmail(chan, useadsi)) {
3445 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
3446 return;
3448 } else
3449 *useadsi = 1;
3452 static void adsi_login(struct ast_channel *chan)
3454 unsigned char buf[256];
3455 int bytes=0;
3456 unsigned char keys[8];
3457 int x;
3458 if (!ast_adsi_available(chan))
3459 return;
3461 for (x=0;x<8;x++)
3462 keys[x] = 0;
3463 /* Set one key for next */
3464 keys[3] = ADSI_KEY_APPS + 3;
3466 bytes += adsi_logo(buf + bytes);
3467 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
3468 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
3469 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3470 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
3471 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
3472 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
3473 bytes += ast_adsi_set_keys(buf + bytes, keys);
3474 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3475 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3478 static void adsi_password(struct ast_channel *chan)
3480 unsigned char buf[256];
3481 int bytes=0;
3482 unsigned char keys[8];
3483 int x;
3484 if (!ast_adsi_available(chan))
3485 return;
3487 for (x=0;x<8;x++)
3488 keys[x] = 0;
3489 /* Set one key for next */
3490 keys[3] = ADSI_KEY_APPS + 3;
3492 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3493 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
3494 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
3495 bytes += ast_adsi_set_keys(buf + bytes, keys);
3496 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3497 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3500 static void adsi_folders(struct ast_channel *chan, int start, char *label)
3502 unsigned char buf[256];
3503 int bytes=0;
3504 unsigned char keys[8];
3505 int x,y;
3507 if (!ast_adsi_available(chan))
3508 return;
3510 for (x=0;x<5;x++) {
3511 y = ADSI_KEY_APPS + 12 + start + x;
3512 if (y > ADSI_KEY_APPS + 12 + 4)
3513 y = 0;
3514 keys[x] = ADSI_KEY_SKT | y;
3516 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
3517 keys[6] = 0;
3518 keys[7] = 0;
3520 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
3521 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
3522 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3523 bytes += ast_adsi_set_keys(buf + bytes, keys);
3524 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3526 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3529 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
3531 int bytes=0;
3532 unsigned char buf[256];
3533 char buf1[256], buf2[256];
3534 char fn2[PATH_MAX];
3536 char cid[256]="";
3537 char *val;
3538 char *name, *num;
3539 char datetime[21]="";
3540 FILE *f;
3542 unsigned char keys[8];
3544 int x;
3546 if (!ast_adsi_available(chan))
3547 return;
3549 /* Retrieve important info */
3550 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
3551 f = fopen(fn2, "r");
3552 if (f) {
3553 while (!feof(f)) {
3554 fgets((char *)buf, sizeof(buf), f);
3555 if (!feof(f)) {
3556 char *stringp=NULL;
3557 stringp = (char *)buf;
3558 strsep(&stringp, "=");
3559 val = strsep(&stringp, "=");
3560 if (!ast_strlen_zero(val)) {
3561 if (!strcmp((char *)buf, "callerid"))
3562 ast_copy_string(cid, val, sizeof(cid));
3563 if (!strcmp((char *)buf, "origdate"))
3564 ast_copy_string(datetime, val, sizeof(datetime));
3568 fclose(f);
3570 /* New meaning for keys */
3571 for (x=0;x<5;x++)
3572 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3573 keys[6] = 0x0;
3574 keys[7] = 0x0;
3576 if (!vms->curmsg) {
3577 /* No prev key, provide "Folder" instead */
3578 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3580 if (vms->curmsg >= vms->lastmsg) {
3581 /* If last message ... */
3582 if (vms->curmsg) {
3583 /* but not only message, provide "Folder" instead */
3584 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3585 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3587 } else {
3588 /* Otherwise if only message, leave blank */
3589 keys[3] = 1;
3593 if (!ast_strlen_zero(cid)) {
3594 ast_callerid_parse(cid, &name, &num);
3595 if (!name)
3596 name = num;
3597 } else
3598 name = "Unknown Caller";
3600 /* If deleted, show "undeleted" */
3602 if (vms->deleted[vms->curmsg])
3603 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3605 /* Except "Exit" */
3606 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3607 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
3608 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
3609 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
3611 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3612 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3613 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
3614 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
3615 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3616 bytes += ast_adsi_set_keys(buf + bytes, keys);
3617 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3619 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3622 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
3624 int bytes=0;
3625 unsigned char buf[256];
3626 unsigned char keys[8];
3628 int x;
3630 if (!ast_adsi_available(chan))
3631 return;
3633 /* New meaning for keys */
3634 for (x=0;x<5;x++)
3635 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3637 keys[6] = 0x0;
3638 keys[7] = 0x0;
3640 if (!vms->curmsg) {
3641 /* No prev key, provide "Folder" instead */
3642 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3644 if (vms->curmsg >= vms->lastmsg) {
3645 /* If last message ... */
3646 if (vms->curmsg) {
3647 /* but not only message, provide "Folder" instead */
3648 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3649 } else {
3650 /* Otherwise if only message, leave blank */
3651 keys[3] = 1;
3655 /* If deleted, show "undeleted" */
3656 if (vms->deleted[vms->curmsg])
3657 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3659 /* Except "Exit" */
3660 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3661 bytes += ast_adsi_set_keys(buf + bytes, keys);
3662 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3664 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3667 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
3669 unsigned char buf[256] = "";
3670 char buf1[256] = "", buf2[256] = "";
3671 int bytes=0;
3672 unsigned char keys[8];
3673 int x;
3675 char *newm = (vms->newmessages == 1) ? "message" : "messages";
3676 char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
3677 if (!ast_adsi_available(chan))
3678 return;
3679 if (vms->newmessages) {
3680 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
3681 if (vms->oldmessages) {
3682 strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
3683 snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
3684 } else {
3685 snprintf(buf2, sizeof(buf2), "%s.", newm);
3687 } else if (vms->oldmessages) {
3688 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
3689 snprintf(buf2, sizeof(buf2), "%s.", oldm);
3690 } else {
3691 strcpy(buf1, "You have no messages.");
3692 buf2[0] = ' ';
3693 buf2[1] = '\0';
3695 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3696 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3697 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3699 for (x=0;x<6;x++)
3700 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3701 keys[6] = 0;
3702 keys[7] = 0;
3704 /* Don't let them listen if there are none */
3705 if (vms->lastmsg < 0)
3706 keys[0] = 1;
3707 bytes += ast_adsi_set_keys(buf + bytes, keys);
3709 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3711 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3714 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
3716 unsigned char buf[256] = "";
3717 char buf1[256] = "", buf2[256] = "";
3718 int bytes=0;
3719 unsigned char keys[8];
3720 int x;
3722 char *mess = (vms->lastmsg == 0) ? "message" : "messages";
3724 if (!ast_adsi_available(chan))
3725 return;
3727 /* Original command keys */
3728 for (x=0;x<6;x++)
3729 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3731 keys[6] = 0;
3732 keys[7] = 0;
3734 if ((vms->lastmsg + 1) < 1)
3735 keys[0] = 0;
3737 snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
3738 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
3740 if (vms->lastmsg + 1)
3741 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
3742 else
3743 strcpy(buf2, "no messages.");
3744 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3745 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3746 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
3747 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3748 bytes += ast_adsi_set_keys(buf + bytes, keys);
3750 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3752 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3757 static void adsi_clear(struct ast_channel *chan)
3759 char buf[256];
3760 int bytes=0;
3761 if (!ast_adsi_available(chan))
3762 return;
3763 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3764 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3766 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3770 static void adsi_goodbye(struct ast_channel *chan)
3772 unsigned char buf[256];
3773 int bytes=0;
3775 if (!ast_adsi_available(chan))
3776 return;
3777 bytes += adsi_logo(buf + bytes);
3778 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
3779 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
3780 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3781 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3783 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3786 /*--- get_folder: Folder menu ---*/
3787 /* Plays "press 1 for INBOX messages" etc
3788 Should possibly be internationalized
3790 static int get_folder(struct ast_channel *chan, int start)
3792 int x;
3793 int d;
3794 char fn[PATH_MAX];
3795 d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
3796 if (d)
3797 return d;
3798 for (x = start; x< 5; x++) { /* For all folders */
3799 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
3800 return d;
3801 d = ast_play_and_wait(chan, "vm-for"); /* "for" */
3802 if (d)
3803 return d;
3804 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
3805 d = vm_play_folder_name(chan, fn);
3806 if (d)
3807 return d;
3808 d = ast_waitfordigit(chan, 500);
3809 if (d)
3810 return d;
3812 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
3813 if (d)
3814 return d;
3815 d = ast_waitfordigit(chan, 4000);
3816 return d;
3819 static int get_folder2(struct ast_channel *chan, char *fn, int start)
3821 int res = 0;
3822 res = ast_play_and_wait(chan, fn); /* Folder name */
3823 while (((res < '0') || (res > '9')) &&
3824 (res != '#') && (res >= 0)) {
3825 res = get_folder(chan, 0);
3827 return res;
3830 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfmts,
3831 char *context, signed char record_gain, long *duration, struct vm_state *vms)
3833 int cmd = 0;
3834 int retries = 0, prepend_duration = 0, already_recorded = 0;
3835 signed char zero_gain = 0;
3836 struct ast_config *msg_cfg;
3837 const char *duration_str;
3838 char msgfile[PATH_MAX], backup[PATH_MAX];
3839 char textfile[PATH_MAX];
3841 /* Must always populate duration correctly */
3842 make_file(msgfile, sizeof(msgfile), curdir, curmsg);
3843 strcpy(textfile, msgfile);
3844 strcpy(backup, msgfile);
3845 strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
3846 strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
3848 if (!(msg_cfg = ast_config_load(textfile))) {
3849 return -1;
3852 *duration = 0;
3853 if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
3854 *duration = atoi(duration_str);
3856 while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
3857 if (cmd)
3858 retries = 0;
3859 switch (cmd) {
3860 case '1':
3861 /* prepend a message to the current message, update the metadata and return */
3863 prepend_duration = 0;
3865 /* if we can't read the message metadata, stop now */
3866 if (!msg_cfg) {
3867 cmd = 0;
3868 break;
3871 /* Back up the original file, so we can retry the prepend */
3872 if (already_recorded)
3873 ast_filecopy(backup, msgfile, NULL);
3874 else
3875 ast_filecopy(msgfile, backup, NULL);
3876 already_recorded = 1;
3878 if (record_gain)
3879 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
3881 cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vmfmts, &prepend_duration, 1, silencethreshold, maxsilence);
3882 if (record_gain)
3883 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
3885 if (prepend_duration) {
3886 struct ast_category *msg_cat;
3887 /* need enough space for a maximum-length message duration */
3888 char duration_str[12];
3890 prepend_duration += *duration;
3891 msg_cat = ast_category_get(msg_cfg, "message");
3892 snprintf(duration_str, 11, "%d", prepend_duration);
3893 if (!ast_variable_update(msg_cat, "duration", duration_str, NULL, 0)) {
3894 config_text_file_save(textfile, msg_cfg, "app_voicemail");
3895 STORE(curdir, vmu->mailbox, context, curmsg, chan, vmu, vmfmts, prepend_duration, vms);
3899 break;
3901 case '2':
3902 cmd = 't';
3903 break;
3904 case '*':
3905 cmd = '*';
3906 break;
3907 default:
3908 cmd = ast_play_and_wait(chan,"vm-forwardoptions");
3909 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
3910 if (!cmd)
3911 cmd = ast_play_and_wait(chan,"vm-starmain");
3912 /* "press star to return to the main menu" */
3913 if (!cmd)
3914 cmd = ast_waitfordigit(chan,6000);
3915 if (!cmd)
3916 retries++;
3917 if (retries > 3)
3918 cmd = 't';
3922 ast_config_destroy(msg_cfg);
3923 if (already_recorded)
3924 ast_filedelete(backup, NULL);
3925 if (prepend_duration)
3926 *duration = prepend_duration;
3928 if (cmd == 't' || cmd == 'S')
3929 cmd = 0;
3930 return cmd;
3933 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
3935 char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
3936 int newmsgs = 0, oldmsgs = 0;
3937 const char *category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3939 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
3940 make_file(fn, sizeof(fn), todir, msgnum);
3941 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
3943 if (!ast_strlen_zero(vmu->attachfmt)) {
3944 if (strstr(fmt, vmu->attachfmt)) {
3945 fmt = vmu->attachfmt;
3946 } else {
3947 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);
3951 /* Attach only the first format */
3952 fmt = ast_strdupa(fmt);
3953 stringp = fmt;
3954 strsep(&stringp, "|");
3956 if (!ast_strlen_zero(vmu->email)) {
3957 int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
3958 char *myserveremail = serveremail;
3959 attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
3960 if (!ast_strlen_zero(vmu->serveremail))
3961 myserveremail = vmu->serveremail;
3963 if (attach_user_voicemail)
3964 RETRIEVE(todir, msgnum);
3966 /*XXX possible imap issue, should category be NULL XXX*/
3967 sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail, chan, category);
3969 if (attach_user_voicemail)
3970 DISPOSE(todir, msgnum);
3973 if (!ast_strlen_zero(vmu->pager)) {
3974 char *myserveremail = serveremail;
3975 if (!ast_strlen_zero(vmu->serveremail))
3976 myserveremail = vmu->serveremail;
3977 sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, duration, vmu, category);
3980 if (ast_test_flag(vmu, VM_DELETE)) {
3981 DELETE(todir, msgnum, fn);
3984 #ifdef IMAP_STORAGE
3985 DELETE(todir, msgnum, fn);
3986 #endif
3987 /* Leave voicemail for someone */
3988 if (ast_app_has_voicemail(ext_context, NULL)) {
3989 ast_app_inboxcount(ext_context, &newmsgs, &oldmsgs);
3991 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);
3992 run_externnotify(vmu->context, vmu->mailbox);
3993 return 0;
3996 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)
3998 #ifdef IMAP_STORAGE
3999 BODY *body;
4000 char *header_content;
4001 char *temp;
4002 char todir[256];
4003 int todircount=0;
4004 struct vm_state *dstvms;
4005 #endif
4006 char username[70]="";
4007 int res = 0, cmd = 0;
4008 struct ast_vm_user *receiver = NULL, *vmtmp;
4009 AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
4010 char *stringp;
4011 const char *s;
4012 int saved_messages = 0, found = 0;
4013 int valid_extensions = 0;
4014 char *dir;
4015 int curmsg;
4017 if (vms == NULL) return -1;
4018 dir = vms->curdir;
4019 curmsg = vms->curmsg;
4021 while (!res && !valid_extensions) {
4022 int use_directory = 0;
4023 if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
4024 int done = 0;
4025 int retries = 0;
4026 cmd=0;
4027 while ((cmd >= 0) && !done ){
4028 if (cmd)
4029 retries = 0;
4030 switch (cmd) {
4031 case '1':
4032 use_directory = 0;
4033 done = 1;
4034 break;
4035 case '2':
4036 use_directory = 1;
4037 done=1;
4038 break;
4039 case '*':
4040 cmd = 't';
4041 done = 1;
4042 break;
4043 default:
4044 /* Press 1 to enter an extension press 2 to use the directory */
4045 cmd = ast_play_and_wait(chan,"vm-forward");
4046 if (!cmd)
4047 cmd = ast_waitfordigit(chan,3000);
4048 if (!cmd)
4049 retries++;
4050 if (retries > 3)
4052 cmd = 't';
4053 done = 1;
4058 if (cmd < 0 || cmd == 't')
4059 break;
4062 if (use_directory) {
4063 /* use app_directory */
4065 char old_context[sizeof(chan->context)];
4066 char old_exten[sizeof(chan->exten)];
4067 int old_priority;
4068 struct ast_app* app;
4071 app = pbx_findapp("Directory");
4072 if (app) {
4073 char vmcontext[256];
4074 /* make backup copies */
4075 memcpy(old_context, chan->context, sizeof(chan->context));
4076 memcpy(old_exten, chan->exten, sizeof(chan->exten));
4077 old_priority = chan->priority;
4079 /* call the the Directory, changes the channel */
4080 snprintf(vmcontext, sizeof(vmcontext), "%s||v", context ? context : "default");
4081 res = pbx_exec(chan, app, vmcontext);
4083 ast_copy_string(username, chan->exten, sizeof(username));
4085 /* restore the old context, exten, and priority */
4086 memcpy(chan->context, old_context, sizeof(chan->context));
4087 memcpy(chan->exten, old_exten, sizeof(chan->exten));
4088 chan->priority = old_priority;
4090 } else {
4091 ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
4092 ast_clear_flag((&globalflags), VM_DIRECFORWARD);
4094 } else {
4095 /* Ask for an extension */
4096 res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
4097 if (res)
4098 break;
4099 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
4100 break;
4103 /* start all over if no username */
4104 if (ast_strlen_zero(username))
4105 continue;
4106 stringp = username;
4107 s = strsep(&stringp, "*");
4108 /* start optimistic */
4109 valid_extensions = 1;
4110 while (s) {
4111 /* 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 */
4112 if ((flag == 1 || strcmp(s,sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
4113 AST_LIST_INSERT_HEAD(&extensions, receiver, list);
4114 found++;
4115 } else {
4116 valid_extensions = 0;
4117 break;
4119 s = strsep(&stringp, "*");
4121 /* break from the loop of reading the extensions */
4122 if (valid_extensions)
4123 break;
4124 /* "I am sorry, that's not a valid extension. Please try again." */
4125 res = ast_play_and_wait(chan, "pbx-invalid");
4127 /* check if we're clear to proceed */
4128 if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
4129 return res;
4130 if (flag==1) {
4131 struct leave_vm_options leave_options;
4132 char mailbox[AST_MAX_EXTENSION * 2 + 2];
4133 /* Make sure that context doesn't get set as a literal "(null)" (or else find_user won't find it) */
4134 if (context)
4135 snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
4136 else
4137 ast_copy_string(mailbox, username, sizeof(mailbox));
4139 /* Send VoiceMail */
4140 memset(&leave_options, 0, sizeof(leave_options));
4141 leave_options.record_gain = record_gain;
4142 cmd = leave_voicemail(chan, mailbox, &leave_options);
4143 } else {
4144 /* Forward VoiceMail */
4145 long duration = 0;
4146 char origmsgfile[PATH_MAX], msgfile[PATH_MAX];
4147 struct vm_state vmstmp;
4149 memcpy(&vmstmp, vms, sizeof(vmstmp));
4151 make_file(origmsgfile, sizeof(origmsgfile), dir, curmsg);
4152 create_dirpath(vmstmp.curdir, sizeof(vmstmp.curdir), sender->context, vmstmp.username, "tmp");
4153 make_file(msgfile, sizeof(msgfile), vmstmp.curdir, curmsg);
4155 RETRIEVE(dir, curmsg);
4157 /* Alter a surrogate file, only */
4158 copy_plain_file(origmsgfile, msgfile);
4160 cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp);
4161 if (!cmd) {
4162 AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
4163 #ifdef IMAP_STORAGE
4164 char *myserveremail;
4165 int attach_user_voicemail;
4166 /* Need to get message content */
4167 if (option_debug > 2)
4168 ast_log (LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", vms->curmsg, vms->msgArray[vms->curmsg]);
4169 if (vms->msgArray[vms->curmsg] == 0) {
4170 ast_log (LOG_WARNING,"Trying to access unknown message\n");
4171 return -1;
4174 /* This will only work for new messages... */
4175 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
4176 /* empty string means no valid header */
4177 if (ast_strlen_zero(header_content)) {
4178 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
4179 return -1;
4181 /* Get header info needed by sendmail */
4182 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:");
4183 if (temp)
4184 duration = atoi(temp);
4185 else
4186 duration = 0;
4188 /* Attach only the first format */
4189 fmt = ast_strdupa(fmt);
4190 if (fmt) {
4191 stringp = fmt;
4192 strsep(&stringp, "|");
4193 } else {
4194 ast_log (LOG_ERROR,"audio format not set. Default to WAV\n");
4195 fmt = "WAV";
4197 if (!strcasecmp(fmt, "wav49"))
4198 fmt = "WAV";
4199 if (option_debug > 2)
4200 ast_log (LOG_DEBUG,"**** format set to %s, vmfmts set to %s\n",fmt,vmfmts);
4201 /* ast_copy_string(fmt, vmfmts, sizeof(fmt));*/
4202 /* if (!ast_strlen_zero(fmt)) { */
4203 snprintf(todir, sizeof(todir), "%s%s/%s/tmp", VM_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
4204 make_gsm_file(vms->fn, sizeof(vms->fn), vms->imapuser, todir, vms->curmsg);
4205 if (option_debug > 2)
4206 ast_log (LOG_DEBUG,"Before mail_fetchstructure, message number is %ld, filename is:%s\n",vms->msgArray[vms->curmsg], vms->fn);
4207 /*mail_fetchstructure (mailstream, vmArray[0], &body); */
4208 mail_fetchstructure (vms->mailstream, vms->msgArray[vms->curmsg], &body);
4209 save_body(body,vms,"3","gsm");
4210 /* should not assume "fmt" here! */
4211 save_body(body,vms,"2",fmt);
4213 /* get destination mailbox */
4214 dstvms = get_vm_state_by_mailbox(vmtmp->mailbox,0);
4215 if (dstvms) {
4216 init_mailstream(dstvms, 0);
4217 if (!dstvms->mailstream) {
4218 ast_log (LOG_ERROR,"IMAP mailstream for %s is NULL\n",vmtmp->mailbox);
4219 } else {
4220 STORE(todir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms);
4221 run_externnotify(vmtmp->context, vmtmp->mailbox);
4223 } else {
4224 ast_log (LOG_ERROR,"Could not find state information for mailbox %s\n",vmtmp->mailbox);
4227 myserveremail = serveremail;
4228 if (!ast_strlen_zero(vmtmp->serveremail))
4229 myserveremail = vmtmp->serveremail;
4230 attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
4231 attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
4232 /* NULL category for IMAP storage */
4233 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);
4234 #else
4235 copy_message(chan, sender, -1, curmsg, duration, vmtmp, fmt, vmstmp.curdir);
4236 #endif
4237 saved_messages++;
4238 AST_LIST_REMOVE_CURRENT(&extensions, list);
4239 free_user(vmtmp);
4240 if (res)
4241 break;
4243 AST_LIST_TRAVERSE_SAFE_END;
4244 if (saved_messages > 0) {
4245 /* give confirmation that the message was saved */
4246 /* commented out since we can't forward batches yet
4247 if (saved_messages == 1)
4248 res = ast_play_and_wait(chan, "vm-message");
4249 else
4250 res = ast_play_and_wait(chan, "vm-messages");
4251 if (!res)
4252 res = ast_play_and_wait(chan, "vm-saved"); */
4253 res = ast_play_and_wait(chan, "vm-msgsaved");
4257 /* Remove surrogate file */
4258 vm_delete(msgfile);
4261 /* If anything failed above, we still have this list to free */
4262 while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list)))
4263 free_user(vmtmp);
4264 return res ? res : cmd;
4267 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
4269 int res;
4270 if ((res = ast_stream_and_wait(chan, file, chan->language, AST_DIGIT_ANY)) < 0)
4271 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
4272 return res;
4275 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
4277 return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", "2", skipms);
4280 static int play_message_category(struct ast_channel *chan, const char *category)
4282 int res = 0;
4284 if (!ast_strlen_zero(category))
4285 res = ast_play_and_wait(chan, category);
4287 if (res) {
4288 ast_log(LOG_WARNING, "No sound file for category '%s' was found.\n", category);
4289 res = 0;
4292 return res;
4295 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
4297 int res = 0;
4298 struct vm_zone *the_zone = NULL;
4299 time_t t;
4301 if (ast_get_time_t(origtime, &t, 0, NULL)) {
4302 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
4303 return 0;
4306 /* Does this user have a timezone specified? */
4307 if (!ast_strlen_zero(vmu->zonetag)) {
4308 /* Find the zone in the list */
4309 struct vm_zone *z;
4310 AST_LIST_LOCK(&zones);
4311 AST_LIST_TRAVERSE(&zones, z, list) {
4312 if (!strcmp(z->name, vmu->zonetag)) {
4313 the_zone = z;
4314 break;
4317 AST_LIST_UNLOCK(&zones);
4320 /* No internal variable parsing for now, so we'll comment it out for the time being */
4321 #if 0
4322 /* Set the DIFF_* variables */
4323 ast_localtime(&t, &time_now, NULL);
4324 tv_now = ast_tvnow();
4325 tnow = tv_now.tv_sec;
4326 ast_localtime(&tnow, &time_then, NULL);
4328 /* Day difference */
4329 if (time_now.tm_year == time_then.tm_year)
4330 snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
4331 else
4332 snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
4333 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
4335 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
4336 #endif
4337 if (the_zone)
4338 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
4339 else if (!strcasecmp(chan->language,"pl")) /* POLISH syntax */
4340 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
4341 else if (!strcasecmp(chan->language,"se")) /* SWEDISH syntax */
4342 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
4343 else if (!strcasecmp(chan->language,"no")) /* NORWEGIAN syntax */
4344 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
4345 else if (!strcasecmp(chan->language,"de")) /* GERMAN syntax */
4346 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
4347 else if (!strcasecmp(chan->language,"nl")) /* DUTCH syntax */
4348 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
4349 else if (!strcasecmp(chan->language,"it")) /* ITALIAN syntax */
4350 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);
4351 else if (!strcasecmp(chan->language,"gr"))
4352 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q H 'digits/kai' M ", NULL);
4353 else if (!strcasecmp(chan->language,"pt_BR"))
4354 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);
4355 else
4356 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
4357 #if 0
4358 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
4359 #endif
4360 return res;
4365 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
4367 int res = 0;
4368 int i;
4369 char *callerid, *name;
4370 char prefile[PATH_MAX] = "";
4373 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
4374 /* 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 */
4375 if ((cid == NULL)||(context == NULL))
4376 return res;
4378 /* Strip off caller ID number from name */
4379 if (option_debug > 2)
4380 ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
4381 ast_callerid_parse(cid, &name, &callerid);
4382 if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
4383 /* Check for internal contexts and only */
4384 /* say extension when the call didn't come from an internal context in the list */
4385 for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
4386 if (option_debug > 2)
4387 ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
4388 if ((strcmp(cidinternalcontexts[i], context) == 0))
4389 break;
4391 if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
4392 if (!res) {
4393 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
4394 if (!ast_strlen_zero(prefile)) {
4395 /* See if we can find a recorded name for this person instead of their extension number */
4396 if (ast_fileexists(prefile, NULL, NULL) > 0) {
4397 if (option_verbose > 2)
4398 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
4399 if (!callback)
4400 res = wait_file2(chan, vms, "vm-from");
4401 res = ast_stream_and_wait(chan, prefile, chan->language, "");
4402 } else {
4403 if (option_verbose > 2)
4404 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
4405 /* BB: Say "from extension" as one saying to sound smoother */
4406 if (!callback)
4407 res = wait_file2(chan, vms, "vm-from-extension");
4408 res = ast_say_digit_str(chan, callerid, "", chan->language);
4414 else if (!res){
4415 if (option_debug > 2)
4416 ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
4417 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
4418 if (!callback)
4419 res = wait_file2(chan, vms, "vm-from-phonenumber");
4420 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
4422 } else {
4423 /* Number unknown */
4424 if (option_debug)
4425 ast_log(LOG_DEBUG, "VM-CID: From an unknown number\n");
4426 /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
4427 res = wait_file2(chan, vms, "vm-unknown-caller");
4429 return res;
4432 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
4434 int res = 0;
4435 int durationm;
4436 int durations;
4437 /* Verify that we have a duration for the message */
4438 if (duration == NULL)
4439 return res;
4441 /* Convert from seconds to minutes */
4442 durations=atoi(duration);
4443 durationm=(durations / 60);
4445 if (option_debug > 2)
4446 ast_log(LOG_DEBUG, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
4448 if ((!res) && (durationm >= minduration)) {
4449 res = wait_file2(chan, vms, "vm-duration");
4451 /* POLISH syntax */
4452 if (!strcasecmp(chan->language, "pl")) {
4453 div_t num = div(durationm, 10);
4455 if (durationm == 1) {
4456 res = ast_play_and_wait(chan, "digits/1z");
4457 res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
4458 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
4459 if (num.rem == 2) {
4460 if (!num.quot) {
4461 res = ast_play_and_wait(chan, "digits/2-ie");
4462 } else {
4463 res = say_and_wait(chan, durationm - 2 , chan->language);
4464 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
4466 } else {
4467 res = say_and_wait(chan, durationm, chan->language);
4469 res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
4470 } else {
4471 res = say_and_wait(chan, durationm, chan->language);
4472 res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
4474 /* DEFAULT syntax */
4475 } else {
4476 res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
4477 res = wait_file2(chan, vms, "vm-minutes");
4480 return res;
4483 #ifdef IMAP_STORAGE
4484 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
4486 BODY *body;
4487 char *header_content;
4488 char cid[256];
4489 char context[256];
4490 char origtime[32];
4491 char duration[16];
4492 char category[32];
4493 char todir[PATH_MAX];
4494 int res = 0;
4495 char *attachedfilefmt;
4496 char *temp;
4498 vms->starting = 0;
4499 if (option_debug > 2)
4500 ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
4501 if (vms->msgArray[vms->curmsg] == 0) {
4502 ast_log (LOG_WARNING,"Trying to access unknown message\n");
4503 return -1;
4506 /* This will only work for new messages... */
4507 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
4508 /* empty string means no valid header */
4509 if (ast_strlen_zero(header_content)) {
4510 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
4511 return -1;
4513 snprintf(todir, sizeof(todir), "%s%s/%s/tmp", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
4514 make_gsm_file(vms->fn, sizeof(vms->fn), vms->imapuser, todir, vms->curmsg);
4516 mail_fetchstructure (vms->mailstream,vms->msgArray[vms->curmsg],&body);
4518 /* We have the body, now we extract the file name of the first attachment. */
4519 if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
4520 attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
4521 } else {
4522 ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
4523 return -1;
4526 /* Find the format of the attached file */
4528 strsep(&attachedfilefmt, ".");
4529 if (!attachedfilefmt) {
4530 ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
4531 return -1;
4533 save_body(body, vms, "2", attachedfilefmt);
4535 adsi_message(chan, vms);
4536 if (!vms->curmsg)
4537 res = wait_file2(chan, vms, "vm-first"); /* "First" */
4538 else if (vms->curmsg == vms->lastmsg)
4539 res = wait_file2(chan, vms, "vm-last"); /* "last" */
4540 if (!res) {
4541 res = wait_file2(chan, vms, "vm-message"); /* "message" */
4542 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
4543 if (!res)
4544 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
4548 /* Get info from headers!! */
4549 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:");
4551 if (temp)
4552 ast_copy_string(cid, temp, sizeof(cid));
4553 else
4554 cid[0] = '\0';
4556 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Context:");
4558 if (temp)
4559 ast_copy_string(context, temp, sizeof(context));
4560 else
4561 context[0] = '\0';
4563 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:");
4565 if (temp)
4566 ast_copy_string(origtime, temp, sizeof(origtime));
4567 else
4568 origtime[0] = '\0';
4570 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:");
4572 if (temp)
4573 ast_copy_string(duration,temp, sizeof(duration));
4574 else
4575 duration[0] = '\0';
4577 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Category:");
4579 if (temp)
4580 ast_copy_string(category,temp, sizeof(category));
4581 else
4582 category[0] = '\0';
4584 /*if (!strncasecmp("macro",context,5)) Macro names in contexts are useless for our needs */
4585 /* context = ast_variable_retrieve(msg_cfg, "message","macrocontext"); */
4586 if (res == '1')
4587 res = 0;
4589 if ((!res) && !ast_strlen_zero(category)) {
4590 res = play_message_category(chan, category);
4593 if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)) && origtime[0] != '\0')
4594 res = play_message_datetime(chan, vmu, origtime, "IMAP_STORAGE");
4595 if ((!res) && (ast_test_flag(vmu, VM_SAYCID)) && cid[0] !='\0' && context[0] !='\0')
4596 res = play_message_callerid(chan, vms, cid, context, 0);
4598 if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)) && duration[0] != '\0')
4599 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
4601 /* Allow pressing '1' to skip envelope / callerid */
4602 /* if (res == '1')
4603 res = 0;
4605 /*ast_config_destroy(msg_cfg);*/
4606 res = 0;
4608 if (!res) {
4609 vms->heard[vms->curmsg] = 1;
4610 res = wait_file(chan, vms, vms->fn);
4612 DISPOSE(vms->curdir, vms->curmsg);
4613 DELETE(0, 0, vms->fn);
4614 return res;
4616 #else
4617 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
4619 int res = 0;
4620 char filename[256], *cid;
4621 const char *origtime, *context, *category, *duration;
4622 struct ast_config *msg_cfg;
4624 vms->starting = 0;
4625 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
4626 adsi_message(chan, vms);
4627 if (!vms->curmsg)
4628 res = wait_file2(chan, vms, "vm-first"); /* "First" */
4629 else if (vms->curmsg == vms->lastmsg)
4630 res = wait_file2(chan, vms, "vm-last"); /* "last" */
4631 if (!res) {
4632 /* POLISH syntax */
4633 if (!strcasecmp(chan->language, "pl")) {
4634 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
4635 int ten, one;
4636 char nextmsg[256];
4637 ten = (vms->curmsg + 1) / 10;
4638 one = (vms->curmsg + 1) % 10;
4640 if (vms->curmsg < 20) {
4641 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
4642 res = wait_file2(chan, vms, nextmsg);
4643 } else {
4644 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
4645 res = wait_file2(chan, vms, nextmsg);
4646 if (one > 0) {
4647 if (!res) {
4648 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
4649 res = wait_file2(chan, vms, nextmsg);
4654 if (!res)
4655 res = wait_file2(chan, vms, "vm-message");
4656 } else {
4657 if (!strcasecmp(chan->language, "se")) /* SWEDISH syntax */
4658 res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
4659 else /* DEFAULT syntax */
4660 res = wait_file2(chan, vms, "vm-message");
4661 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
4662 if (!res)
4663 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
4668 /* Retrieve info from VM attribute file */
4669 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
4670 snprintf(filename, sizeof(filename), "%s.txt", vms->fn2);
4671 RETRIEVE(vms->curdir, vms->curmsg);
4672 msg_cfg = ast_config_load(filename);
4673 if (!msg_cfg) {
4674 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
4675 return 0;
4678 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
4679 ast_log(LOG_WARNING, "No origtime?!\n");
4680 DISPOSE(vms->curdir, vms->curmsg);
4681 ast_config_destroy(msg_cfg);
4682 return 0;
4685 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
4686 duration = ast_variable_retrieve(msg_cfg, "message", "duration");
4687 category = ast_variable_retrieve(msg_cfg, "message", "category");
4689 context = ast_variable_retrieve(msg_cfg, "message", "context");
4690 if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
4691 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
4692 if (!res)
4693 res = play_message_category(chan, category);
4694 if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
4695 res = play_message_datetime(chan, vmu, origtime, filename);
4696 if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
4697 res = play_message_callerid(chan, vms, cid, context, 0);
4698 if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
4699 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
4700 /* Allow pressing '1' to skip envelope / callerid */
4701 if (res == '1')
4702 res = 0;
4703 ast_config_destroy(msg_cfg);
4705 if (!res) {
4706 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
4707 vms->heard[vms->curmsg] = 1;
4708 if ((res = wait_file(chan, vms, vms->fn)) < 0) {
4709 ast_log(LOG_WARNING, "Playback of message %s failed\n", vms->fn);
4710 res = 0;
4713 DISPOSE(vms->curdir, vms->curmsg);
4714 return res;
4716 #endif
4718 #ifdef IMAP_STORAGE
4719 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
4721 char tmp[256], *t = tmp;
4722 size_t left = sizeof(tmp);
4724 if (box == 1) {
4725 ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
4726 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(1));
4727 } else {
4728 ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
4729 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
4732 /* Build up server information */
4733 ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
4735 /* Add authentication user if present */
4736 if (!ast_strlen_zero(authuser))
4737 ast_build_string(&t, &left, "/authuser=%s", authuser);
4739 /* Add flags if present */
4740 if (!ast_strlen_zero(imapflags))
4741 ast_build_string(&t, &left, "/%s", imapflags);
4743 /* End with username */
4744 ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
4746 if (box == 0 || box == 1)
4747 snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
4748 else
4749 snprintf(spec, len, "%s%s%c%s", tmp, imapfolder, delimiter, mbox(box));
4752 static int init_mailstream(struct vm_state *vms, int box)
4754 MAILSTREAM *stream = NIL;
4755 long debug;
4756 char tmp[256];
4758 if (!vms) {
4759 ast_log (LOG_ERROR,"vm_state is NULL!\n");
4760 return -1;
4762 if (option_debug > 2)
4763 ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
4764 if (vms->mailstream == NIL || !vms->mailstream) {
4765 if (option_debug)
4766 ast_log (LOG_DEBUG,"mailstream not set.\n");
4767 } else {
4768 stream = vms->mailstream;
4770 /* debug = T; user wants protocol telemetry? */
4771 debug = NIL; /* NO protocol telemetry? */
4773 if (delimiter == '\0') { /* did not probe the server yet */
4774 char *cp;
4775 #ifdef USE_SYSTEM_IMAP
4776 #include <imap/linkage.c>
4777 #elif defined(USE_SYSTEM_CCLIENT)
4778 #include <c-client/linkage.c>
4779 #else
4780 #include "linkage.c"
4781 #endif
4782 /* Connect to INBOX first to get folders delimiter */
4783 imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
4784 stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
4785 if (stream == NIL) {
4786 ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
4787 return -1;
4789 get_mailbox_delimiter(stream);
4790 /* update delimiter in imapfolder */
4791 for (cp = imapfolder; *cp; cp++)
4792 if (*cp == '/')
4793 *cp = delimiter;
4795 /* Now connect to the target folder */
4796 imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
4797 if (option_debug > 2)
4798 ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
4799 vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
4800 if (vms->mailstream == NIL) {
4801 return -1;
4802 } else {
4803 return 0;
4807 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
4809 SEARCHPGM *pgm;
4810 SEARCHHEADER *hdr;
4811 int ret;
4813 ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
4814 if (option_debug > 2)
4815 ast_log(LOG_DEBUG,"Before init_mailstream, user is %s\n",vmu->imapuser);
4816 ret = init_mailstream(vms, box);
4817 if (ret != 0 || !vms->mailstream) {
4818 ast_log (LOG_ERROR,"Could not initialize mailstream\n");
4819 return -1;
4822 /* Check Quota */
4823 if (box == 0) {
4824 if (option_debug > 2)
4825 ast_log(LOG_DEBUG, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
4826 check_quota(vms,(char *)mbox(box));
4829 pgm = mail_newsearchpgm();
4831 /* Check IMAP folder for Asterisk messages only... */
4832 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", vmu->mailbox);
4833 pgm->header = hdr;
4834 pgm->deleted = 0;
4835 pgm->undeleted = 1;
4837 /* if box = 0, check for new, if box = 1, check for read */
4838 if (box == 0) {
4839 pgm->unseen = 1;
4840 pgm->seen = 0;
4841 } else if (box == 1) {
4842 pgm->seen = 1;
4843 pgm->unseen = 0;
4846 vms->vmArrayIndex = 0;
4847 if (option_debug > 2)
4848 ast_log(LOG_DEBUG,"Before mail_search_full, user is %s\n",vmu->imapuser);
4849 mail_search_full (vms->mailstream, NULL, pgm, NIL);
4852 vms->lastmsg = vms->vmArrayIndex - 1;
4854 mail_free_searchpgm(&pgm);
4855 return 0;
4857 #else
4858 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
4860 int res = 0;
4861 int count_msg, last_msg;
4863 ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
4865 /* Rename the member vmbox HERE so that we don't try to return before
4866 * we know what's going on.
4868 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
4870 /* Faster to make the directory than to check if it exists. */
4871 create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
4873 count_msg = count_messages(vmu, vms->curdir);
4874 if (count_msg < 0)
4875 return count_msg;
4876 else
4877 vms->lastmsg = count_msg - 1;
4880 The following test is needed in case sequencing gets messed up.
4881 There appears to be more than one way to mess up sequence, so
4882 we will not try to find all of the root causes--just fix it when
4883 detected.
4886 last_msg = last_message_index(vmu, vms->curdir);
4887 if (last_msg < 0)
4888 return last_msg;
4889 else if (vms->lastmsg != last_msg)
4891 ast_log(LOG_NOTICE, "Resequencing Mailbox: %s\n", vms->curdir);
4892 res = resequence_mailbox(vmu, vms->curdir);
4893 if (res)
4894 return res;
4897 return 0;
4899 #endif
4901 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
4903 int x = 0;
4904 #ifndef IMAP_STORAGE
4905 int res = 0, nummsg;
4906 #endif
4908 if (vms->lastmsg <= -1)
4909 goto done;
4911 vms->curmsg = -1;
4912 #ifndef IMAP_STORAGE
4913 /* Get the deleted messages fixed */
4914 if (vm_lock_path(vms->curdir))
4915 return ERROR_LOCK_PATH;
4917 for (x = 0; x < vmu->maxmsg; x++) {
4918 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
4919 /* Save this message. It's not in INBOX or hasn't been heard */
4920 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
4921 if (!EXISTS(vms->curdir, x, vms->fn, NULL))
4922 break;
4923 vms->curmsg++;
4924 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
4925 if (strcmp(vms->fn, vms->fn2)) {
4926 RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
4928 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
4929 /* Move to old folder before deleting */
4930 res = save_to_folder(vmu, vms, x, 1);
4931 if (res == ERROR_LOCK_PATH || res == ERROR_MAILBOX_FULL) {
4932 /* If save failed do not delete the message */
4933 ast_log(LOG_WARNING, "Save failed. Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
4934 vms->deleted[x] = 0;
4935 vms->heard[x] = 0;
4936 --x;
4941 /* Delete ALL remaining messages */
4942 nummsg = x - 1;
4943 for (x = vms->curmsg + 1; x <= nummsg; x++) {
4944 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
4945 if (EXISTS(vms->curdir, x, vms->fn, NULL))
4946 DELETE(vms->curdir, x, vms->fn);
4948 ast_unlock_path(vms->curdir);
4949 #else
4950 if (vms->deleted) {
4951 for (x=0;x < vmu->maxmsg;x++) {
4952 if (vms->deleted[x]) {
4953 if (option_debug > 2)
4954 ast_log(LOG_DEBUG,"IMAP delete of %d\n",x);
4955 IMAP_DELETE(vms->curdir, x, vms->fn, vms);
4959 #endif
4961 done:
4962 if (vms->deleted)
4963 memset(vms->deleted, 0, vmu->maxmsg * sizeof(int));
4964 if (vms->heard)
4965 memset(vms->heard, 0, vmu->maxmsg * sizeof(int));
4967 return 0;
4970 /* In Greek even though we CAN use a syntax like "friends messages"
4971 * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
4972 * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
4973 * syntax for the above three categories which is more elegant.
4976 static int vm_play_folder_name_gr(struct ast_channel *chan, char *mbox)
4978 int cmd;
4979 char *buf;
4981 buf = alloca(strlen(mbox)+2);
4982 strcpy(buf, mbox);
4983 strcat(buf,"s");
4985 if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")){
4986 cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
4987 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
4988 } else {
4989 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
4990 return cmd ? cmd : ast_play_and_wait(chan, mbox); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
4994 static int vm_play_folder_name_pl(struct ast_channel *chan, char *mbox)
4996 int cmd;
4998 if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")) {
4999 if (!strcasecmp(mbox, "vm-INBOX"))
5000 cmd = ast_play_and_wait(chan, "vm-new-e");
5001 else
5002 cmd = ast_play_and_wait(chan, "vm-old-e");
5003 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
5004 } else {
5005 cmd = ast_play_and_wait(chan, "vm-messages");
5006 return cmd ? cmd : ast_play_and_wait(chan, mbox);
5010 static int vm_play_folder_name_ua(struct ast_channel *chan, char *mbox)
5012 int cmd;
5014 if (!strcasecmp(mbox, "vm-Family") || !strcasecmp(mbox, "vm-Friends") || !strcasecmp(mbox, "vm-Work")){
5015 cmd = ast_play_and_wait(chan, "vm-messages");
5016 return cmd ? cmd : ast_play_and_wait(chan, mbox);
5017 } else {
5018 cmd = ast_play_and_wait(chan, mbox);
5019 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
5023 static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
5025 int cmd;
5027 if (!strcasecmp(chan->language, "it") || !strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* Italian, Spanish, French or Portuguese syntax */
5028 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
5029 return cmd ? cmd : ast_play_and_wait(chan, mbox);
5030 } else if (!strcasecmp(chan->language, "gr")){
5031 return vm_play_folder_name_gr(chan, mbox);
5032 } else if (!strcasecmp(chan->language, "pl")){
5033 return vm_play_folder_name_pl(chan, mbox);
5034 } else if (!strcasecmp(chan->language, "ua")){ /* Ukrainian syntax */
5035 return vm_play_folder_name_ua(chan, mbox);
5036 } else { /* Default English */
5037 cmd = ast_play_and_wait(chan, mbox);
5038 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
5042 /* GREEK SYNTAX
5043 In greek the plural for old/new is
5044 different so we need the following files
5045 We also need vm-denExeteMynhmata because
5046 this syntax is different.
5048 -> vm-Olds.wav : "Palia"
5049 -> vm-INBOXs.wav : "Nea"
5050 -> vm-denExeteMynhmata : "den exete mynhmata"
5054 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
5056 int res = 0;
5058 if (vms->newmessages) {
5059 res = ast_play_and_wait(chan, "vm-youhave");
5060 if (!res)
5061 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
5062 if (!res) {
5063 if ((vms->newmessages == 1)) {
5064 res = ast_play_and_wait(chan, "vm-INBOX");
5065 if (!res)
5066 res = ast_play_and_wait(chan, "vm-message");
5067 } else {
5068 res = ast_play_and_wait(chan, "vm-INBOXs");
5069 if (!res)
5070 res = ast_play_and_wait(chan, "vm-messages");
5073 } else if (vms->oldmessages){
5074 res = ast_play_and_wait(chan, "vm-youhave");
5075 if (!res)
5076 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
5077 if ((vms->oldmessages == 1)){
5078 res = ast_play_and_wait(chan, "vm-Old");
5079 if (!res)
5080 res = ast_play_and_wait(chan, "vm-message");
5081 } else {
5082 res = ast_play_and_wait(chan, "vm-Olds");
5083 if (!res)
5084 res = ast_play_and_wait(chan, "vm-messages");
5086 } else if (!vms->oldmessages && !vms->newmessages)
5087 res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
5088 return res;
5091 /* Default English syntax */
5092 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
5094 int res;
5096 /* Introduce messages they have */
5097 res = ast_play_and_wait(chan, "vm-youhave");
5098 if (!res) {
5099 if (vms->newmessages) {
5100 res = say_and_wait(chan, vms->newmessages, chan->language);
5101 if (!res)
5102 res = ast_play_and_wait(chan, "vm-INBOX");
5103 if (vms->oldmessages && !res)
5104 res = ast_play_and_wait(chan, "vm-and");
5105 else if (!res) {
5106 if ((vms->newmessages == 1))
5107 res = ast_play_and_wait(chan, "vm-message");
5108 else
5109 res = ast_play_and_wait(chan, "vm-messages");
5113 if (!res && vms->oldmessages) {
5114 res = say_and_wait(chan, vms->oldmessages, chan->language);
5115 if (!res)
5116 res = ast_play_and_wait(chan, "vm-Old");
5117 if (!res) {
5118 if (vms->oldmessages == 1)
5119 res = ast_play_and_wait(chan, "vm-message");
5120 else
5121 res = ast_play_and_wait(chan, "vm-messages");
5124 if (!res) {
5125 if (!vms->oldmessages && !vms->newmessages) {
5126 res = ast_play_and_wait(chan, "vm-no");
5127 if (!res)
5128 res = ast_play_and_wait(chan, "vm-messages");
5132 return res;
5135 /* ITALIAN syntax */
5136 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
5138 /* Introduce messages they have */
5139 int res;
5140 if (!vms->oldmessages && !vms->newmessages)
5141 res = ast_play_and_wait(chan, "vm-no") ||
5142 ast_play_and_wait(chan, "vm-message");
5143 else
5144 res = ast_play_and_wait(chan, "vm-youhave");
5145 if (!res && vms->newmessages) {
5146 res = (vms->newmessages == 1) ?
5147 ast_play_and_wait(chan, "digits/un") ||
5148 ast_play_and_wait(chan, "vm-nuovo") ||
5149 ast_play_and_wait(chan, "vm-message") :
5150 /* 2 or more new messages */
5151 say_and_wait(chan, vms->newmessages, chan->language) ||
5152 ast_play_and_wait(chan, "vm-nuovi") ||
5153 ast_play_and_wait(chan, "vm-messages");
5154 if (!res && vms->oldmessages)
5155 res = ast_play_and_wait(chan, "vm-and");
5157 if (!res && vms->oldmessages) {
5158 res = (vms->oldmessages == 1) ?
5159 ast_play_and_wait(chan, "digits/un") ||
5160 ast_play_and_wait(chan, "vm-vecchio") ||
5161 ast_play_and_wait(chan, "vm-message") :
5162 /* 2 or more old messages */
5163 say_and_wait(chan, vms->oldmessages, chan->language) ||
5164 ast_play_and_wait(chan, "vm-vecchi") ||
5165 ast_play_and_wait(chan, "vm-messages");
5167 return res ? -1 : 0;
5170 /* POLISH syntax */
5171 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
5173 /* Introduce messages they have */
5174 int res;
5175 div_t num;
5177 if (!vms->oldmessages && !vms->newmessages) {
5178 res = ast_play_and_wait(chan, "vm-no");
5179 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5180 return res;
5181 } else {
5182 res = ast_play_and_wait(chan, "vm-youhave");
5185 if (vms->newmessages) {
5186 num = div(vms->newmessages, 10);
5187 if (vms->newmessages == 1) {
5188 res = ast_play_and_wait(chan, "digits/1-a");
5189 res = res ? res : ast_play_and_wait(chan, "vm-new-a");
5190 res = res ? res : ast_play_and_wait(chan, "vm-message");
5191 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
5192 if (num.rem == 2) {
5193 if (!num.quot) {
5194 res = ast_play_and_wait(chan, "digits/2-ie");
5195 } else {
5196 res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
5197 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
5199 } else {
5200 res = say_and_wait(chan, vms->newmessages, chan->language);
5202 res = res ? res : ast_play_and_wait(chan, "vm-new-e");
5203 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5204 } else {
5205 res = say_and_wait(chan, vms->newmessages, chan->language);
5206 res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
5207 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5209 if (!res && vms->oldmessages)
5210 res = ast_play_and_wait(chan, "vm-and");
5212 if (!res && vms->oldmessages) {
5213 num = div(vms->oldmessages, 10);
5214 if (vms->oldmessages == 1) {
5215 res = ast_play_and_wait(chan, "digits/1-a");
5216 res = res ? res : ast_play_and_wait(chan, "vm-old-a");
5217 res = res ? res : ast_play_and_wait(chan, "vm-message");
5218 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
5219 if (num.rem == 2) {
5220 if (!num.quot) {
5221 res = ast_play_and_wait(chan, "digits/2-ie");
5222 } else {
5223 res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
5224 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
5226 } else {
5227 res = say_and_wait(chan, vms->oldmessages, chan->language);
5229 res = res ? res : ast_play_and_wait(chan, "vm-old-e");
5230 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5231 } else {
5232 res = say_and_wait(chan, vms->oldmessages, chan->language);
5233 res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
5234 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5238 return res;
5241 /* SWEDISH syntax */
5242 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
5244 /* Introduce messages they have */
5245 int res;
5247 res = ast_play_and_wait(chan, "vm-youhave");
5248 if (res)
5249 return res;
5251 if (!vms->oldmessages && !vms->newmessages) {
5252 res = ast_play_and_wait(chan, "vm-no");
5253 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5254 return res;
5257 if (vms->newmessages) {
5258 if ((vms->newmessages == 1)) {
5259 res = ast_play_and_wait(chan, "digits/ett");
5260 res = res ? res : ast_play_and_wait(chan, "vm-nytt");
5261 res = res ? res : ast_play_and_wait(chan, "vm-message");
5262 } else {
5263 res = say_and_wait(chan, vms->newmessages, chan->language);
5264 res = res ? res : ast_play_and_wait(chan, "vm-nya");
5265 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5267 if (!res && vms->oldmessages)
5268 res = ast_play_and_wait(chan, "vm-and");
5270 if (!res && vms->oldmessages) {
5271 if (vms->oldmessages == 1) {
5272 res = ast_play_and_wait(chan, "digits/ett");
5273 res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
5274 res = res ? res : ast_play_and_wait(chan, "vm-message");
5275 } else {
5276 res = say_and_wait(chan, vms->oldmessages, chan->language);
5277 res = res ? res : ast_play_and_wait(chan, "vm-gamla");
5278 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5282 return res;
5285 /* NORWEGIAN syntax */
5286 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
5288 /* Introduce messages they have */
5289 int res;
5291 res = ast_play_and_wait(chan, "vm-youhave");
5292 if (res)
5293 return res;
5295 if (!vms->oldmessages && !vms->newmessages) {
5296 res = ast_play_and_wait(chan, "vm-no");
5297 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5298 return res;
5301 if (vms->newmessages) {
5302 if ((vms->newmessages == 1)) {
5303 res = ast_play_and_wait(chan, "digits/1");
5304 res = res ? res : ast_play_and_wait(chan, "vm-ny");
5305 res = res ? res : ast_play_and_wait(chan, "vm-message");
5306 } else {
5307 res = say_and_wait(chan, vms->newmessages, chan->language);
5308 res = res ? res : ast_play_and_wait(chan, "vm-nye");
5309 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5311 if (!res && vms->oldmessages)
5312 res = ast_play_and_wait(chan, "vm-and");
5314 if (!res && vms->oldmessages) {
5315 if (vms->oldmessages == 1) {
5316 res = ast_play_and_wait(chan, "digits/1");
5317 res = res ? res : ast_play_and_wait(chan, "vm-gamel");
5318 res = res ? res : ast_play_and_wait(chan, "vm-message");
5319 } else {
5320 res = say_and_wait(chan, vms->oldmessages, chan->language);
5321 res = res ? res : ast_play_and_wait(chan, "vm-gamle");
5322 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5326 return res;
5329 /* GERMAN syntax */
5330 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
5332 /* Introduce messages they have */
5333 int res;
5334 res = ast_play_and_wait(chan, "vm-youhave");
5335 if (!res) {
5336 if (vms->newmessages) {
5337 if ((vms->newmessages == 1))
5338 res = ast_play_and_wait(chan, "digits/1F");
5339 else
5340 res = say_and_wait(chan, vms->newmessages, chan->language);
5341 if (!res)
5342 res = ast_play_and_wait(chan, "vm-INBOX");
5343 if (vms->oldmessages && !res)
5344 res = ast_play_and_wait(chan, "vm-and");
5345 else if (!res) {
5346 if ((vms->newmessages == 1))
5347 res = ast_play_and_wait(chan, "vm-message");
5348 else
5349 res = ast_play_and_wait(chan, "vm-messages");
5353 if (!res && vms->oldmessages) {
5354 if (vms->oldmessages == 1)
5355 res = ast_play_and_wait(chan, "digits/1F");
5356 else
5357 res = say_and_wait(chan, vms->oldmessages, chan->language);
5358 if (!res)
5359 res = ast_play_and_wait(chan, "vm-Old");
5360 if (!res) {
5361 if (vms->oldmessages == 1)
5362 res = ast_play_and_wait(chan, "vm-message");
5363 else
5364 res = ast_play_and_wait(chan, "vm-messages");
5367 if (!res) {
5368 if (!vms->oldmessages && !vms->newmessages) {
5369 res = ast_play_and_wait(chan, "vm-no");
5370 if (!res)
5371 res = ast_play_and_wait(chan, "vm-messages");
5375 return res;
5378 /* SPANISH syntax */
5379 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
5381 /* Introduce messages they have */
5382 int res;
5383 if (!vms->oldmessages && !vms->newmessages) {
5384 res = ast_play_and_wait(chan, "vm-youhaveno");
5385 if (!res)
5386 res = ast_play_and_wait(chan, "vm-messages");
5387 } else {
5388 res = ast_play_and_wait(chan, "vm-youhave");
5390 if (!res) {
5391 if (vms->newmessages) {
5392 if (!res) {
5393 if ((vms->newmessages == 1)) {
5394 res = ast_play_and_wait(chan, "digits/1M");
5395 if (!res)
5396 res = ast_play_and_wait(chan, "vm-message");
5397 if (!res)
5398 res = ast_play_and_wait(chan, "vm-INBOXs");
5399 } else {
5400 res = say_and_wait(chan, vms->newmessages, chan->language);
5401 if (!res)
5402 res = ast_play_and_wait(chan, "vm-messages");
5403 if (!res)
5404 res = ast_play_and_wait(chan, "vm-INBOX");
5407 if (vms->oldmessages && !res)
5408 res = ast_play_and_wait(chan, "vm-and");
5410 if (vms->oldmessages) {
5411 if (!res) {
5412 if (vms->oldmessages == 1) {
5413 res = ast_play_and_wait(chan, "digits/1M");
5414 if (!res)
5415 res = ast_play_and_wait(chan, "vm-message");
5416 if (!res)
5417 res = ast_play_and_wait(chan, "vm-Olds");
5418 } else {
5419 res = say_and_wait(chan, vms->oldmessages, chan->language);
5420 if (!res)
5421 res = ast_play_and_wait(chan, "vm-messages");
5422 if (!res)
5423 res = ast_play_and_wait(chan, "vm-Old");
5428 return res;
5431 /* BRAZILIAN PORTUGUESE syntax */
5432 static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
5433 /* Introduce messages they have */
5434 int res;
5435 if (!vms->oldmessages && !vms->newmessages) {
5436 res = ast_play_and_wait(chan, "vm-nomessages");
5437 return res;
5439 else {
5440 res = ast_play_and_wait(chan, "vm-youhave");
5442 if (vms->newmessages) {
5443 if (!res)
5444 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
5445 if ((vms->newmessages == 1)) {
5446 if (!res)
5447 res = ast_play_and_wait(chan, "vm-message");
5448 if (!res)
5449 res = ast_play_and_wait(chan, "vm-INBOXs");
5451 else {
5452 if (!res)
5453 res = ast_play_and_wait(chan, "vm-messages");
5454 if (!res)
5455 res = ast_play_and_wait(chan, "vm-INBOX");
5457 if (vms->oldmessages && !res)
5458 res = ast_play_and_wait(chan, "vm-and");
5460 if (vms->oldmessages) {
5461 if (!res)
5462 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
5463 if (vms->oldmessages == 1) {
5464 if (!res)
5465 res = ast_play_and_wait(chan, "vm-message");
5466 if (!res)
5467 res = ast_play_and_wait(chan, "vm-Olds");
5469 else {
5470 if (!res)
5471 res = ast_play_and_wait(chan, "vm-messages");
5472 if (!res)
5473 res = ast_play_and_wait(chan, "vm-Old");
5476 return res;
5479 /* FRENCH syntax */
5480 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
5482 /* Introduce messages they have */
5483 int res;
5484 res = ast_play_and_wait(chan, "vm-youhave");
5485 if (!res) {
5486 if (vms->newmessages) {
5487 res = say_and_wait(chan, vms->newmessages, chan->language);
5488 if (!res)
5489 res = ast_play_and_wait(chan, "vm-INBOX");
5490 if (vms->oldmessages && !res)
5491 res = ast_play_and_wait(chan, "vm-and");
5492 else if (!res) {
5493 if ((vms->newmessages == 1))
5494 res = ast_play_and_wait(chan, "vm-message");
5495 else
5496 res = ast_play_and_wait(chan, "vm-messages");
5500 if (!res && vms->oldmessages) {
5501 res = say_and_wait(chan, vms->oldmessages, chan->language);
5502 if (!res)
5503 res = ast_play_and_wait(chan, "vm-Old");
5504 if (!res) {
5505 if (vms->oldmessages == 1)
5506 res = ast_play_and_wait(chan, "vm-message");
5507 else
5508 res = ast_play_and_wait(chan, "vm-messages");
5511 if (!res) {
5512 if (!vms->oldmessages && !vms->newmessages) {
5513 res = ast_play_and_wait(chan, "vm-no");
5514 if (!res)
5515 res = ast_play_and_wait(chan, "vm-messages");
5519 return res;
5522 /* DUTCH syntax */
5523 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
5525 /* Introduce messages they have */
5526 int res;
5527 res = ast_play_and_wait(chan, "vm-youhave");
5528 if (!res) {
5529 if (vms->newmessages) {
5530 res = say_and_wait(chan, vms->newmessages, chan->language);
5531 if (!res) {
5532 if (vms->newmessages == 1)
5533 res = ast_play_and_wait(chan, "vm-INBOXs");
5534 else
5535 res = ast_play_and_wait(chan, "vm-INBOX");
5537 if (vms->oldmessages && !res)
5538 res = ast_play_and_wait(chan, "vm-and");
5539 else if (!res) {
5540 if ((vms->newmessages == 1))
5541 res = ast_play_and_wait(chan, "vm-message");
5542 else
5543 res = ast_play_and_wait(chan, "vm-messages");
5547 if (!res && vms->oldmessages) {
5548 res = say_and_wait(chan, vms->oldmessages, chan->language);
5549 if (!res) {
5550 if (vms->oldmessages == 1)
5551 res = ast_play_and_wait(chan, "vm-Olds");
5552 else
5553 res = ast_play_and_wait(chan, "vm-Old");
5555 if (!res) {
5556 if (vms->oldmessages == 1)
5557 res = ast_play_and_wait(chan, "vm-message");
5558 else
5559 res = ast_play_and_wait(chan, "vm-messages");
5562 if (!res) {
5563 if (!vms->oldmessages && !vms->newmessages) {
5564 res = ast_play_and_wait(chan, "vm-no");
5565 if (!res)
5566 res = ast_play_and_wait(chan, "vm-messages");
5570 return res;
5573 /* PORTUGUESE syntax */
5574 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
5576 /* Introduce messages they have */
5577 int res;
5578 res = ast_play_and_wait(chan, "vm-youhave");
5579 if (!res) {
5580 if (vms->newmessages) {
5581 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
5582 if (!res) {
5583 if ((vms->newmessages == 1)) {
5584 res = ast_play_and_wait(chan, "vm-message");
5585 if (!res)
5586 res = ast_play_and_wait(chan, "vm-INBOXs");
5587 } else {
5588 res = ast_play_and_wait(chan, "vm-messages");
5589 if (!res)
5590 res = ast_play_and_wait(chan, "vm-INBOX");
5593 if (vms->oldmessages && !res)
5594 res = ast_play_and_wait(chan, "vm-and");
5596 if (!res && vms->oldmessages) {
5597 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
5598 if (!res) {
5599 if (vms->oldmessages == 1) {
5600 res = ast_play_and_wait(chan, "vm-message");
5601 if (!res)
5602 res = ast_play_and_wait(chan, "vm-Olds");
5603 } else {
5604 res = ast_play_and_wait(chan, "vm-messages");
5605 if (!res)
5606 res = ast_play_and_wait(chan, "vm-Old");
5610 if (!res) {
5611 if (!vms->oldmessages && !vms->newmessages) {
5612 res = ast_play_and_wait(chan, "vm-no");
5613 if (!res)
5614 res = ast_play_and_wait(chan, "vm-messages");
5618 return res;
5622 /* CZECH syntax */
5623 /* in czech there must be declension of word new and message
5624 * czech : english : czech : english
5625 * --------------------------------------------------------
5626 * vm-youhave : you have
5627 * vm-novou : one new : vm-zpravu : message
5628 * vm-nove : 2-4 new : vm-zpravy : messages
5629 * vm-novych : 5-infinite new : vm-zprav : messages
5630 * vm-starou : one old
5631 * vm-stare : 2-4 old
5632 * vm-starych : 5-infinite old
5633 * jednu : one - falling 4.
5634 * vm-no : no ( no messages )
5637 static int vm_intro_cz(struct ast_channel *chan,struct vm_state *vms)
5639 int res;
5640 res = ast_play_and_wait(chan, "vm-youhave");
5641 if (!res) {
5642 if (vms->newmessages) {
5643 if (vms->newmessages == 1) {
5644 res = ast_play_and_wait(chan, "digits/jednu");
5645 } else {
5646 res = say_and_wait(chan, vms->newmessages, chan->language);
5648 if (!res) {
5649 if ((vms->newmessages == 1))
5650 res = ast_play_and_wait(chan, "vm-novou");
5651 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
5652 res = ast_play_and_wait(chan, "vm-nove");
5653 if (vms->newmessages > 4)
5654 res = ast_play_and_wait(chan, "vm-novych");
5656 if (vms->oldmessages && !res)
5657 res = ast_play_and_wait(chan, "vm-and");
5658 else if (!res) {
5659 if ((vms->newmessages == 1))
5660 res = ast_play_and_wait(chan, "vm-zpravu");
5661 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
5662 res = ast_play_and_wait(chan, "vm-zpravy");
5663 if (vms->newmessages > 4)
5664 res = ast_play_and_wait(chan, "vm-zprav");
5667 if (!res && vms->oldmessages) {
5668 res = say_and_wait(chan, vms->oldmessages, chan->language);
5669 if (!res) {
5670 if ((vms->oldmessages == 1))
5671 res = ast_play_and_wait(chan, "vm-starou");
5672 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
5673 res = ast_play_and_wait(chan, "vm-stare");
5674 if (vms->oldmessages > 4)
5675 res = ast_play_and_wait(chan, "vm-starych");
5677 if (!res) {
5678 if ((vms->oldmessages == 1))
5679 res = ast_play_and_wait(chan, "vm-zpravu");
5680 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
5681 res = ast_play_and_wait(chan, "vm-zpravy");
5682 if (vms->oldmessages > 4)
5683 res = ast_play_and_wait(chan, "vm-zprav");
5686 if (!res) {
5687 if (!vms->oldmessages && !vms->newmessages) {
5688 res = ast_play_and_wait(chan, "vm-no");
5689 if (!res)
5690 res = ast_play_and_wait(chan, "vm-zpravy");
5694 return res;
5697 static int get_lastdigits(int num)
5699 num %= 100;
5700 return (num < 20) ? num : num % 10;
5703 static int vm_intro_ru(struct ast_channel *chan,struct vm_state *vms)
5705 int res;
5706 int lastnum = 0;
5707 int dcnum;
5709 res = ast_play_and_wait(chan, "vm-youhave");
5710 if (!res && vms->newmessages) {
5711 lastnum = get_lastdigits(vms->newmessages);
5712 dcnum = vms->newmessages - lastnum;
5713 if (dcnum)
5714 res = say_and_wait(chan, dcnum, chan->language);
5715 if (!res && lastnum) {
5716 if (lastnum == 1)
5717 res = ast_play_and_wait(chan, "digits/ru/odno");
5718 else
5719 res = say_and_wait(chan, lastnum, chan->language);
5722 if (!res)
5723 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-novoe" : "vm-novyh");
5725 if (!res && vms->oldmessages)
5726 res = ast_play_and_wait(chan, "vm-and");
5729 if (!res && vms->oldmessages) {
5730 lastnum = get_lastdigits(vms->oldmessages);
5731 dcnum = vms->oldmessages - lastnum;
5732 if (dcnum)
5733 res = say_and_wait(chan, dcnum, chan->language);
5734 if (!res && lastnum) {
5735 if (lastnum == 1)
5736 res = ast_play_and_wait(chan, "digits/ru/odno");
5737 else
5738 res = say_and_wait(chan, lastnum, chan->language);
5741 if (!res)
5742 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-staroe" : "vm-staryh");
5745 if (!res && !vms->newmessages && !vms->oldmessages) {
5746 lastnum = 0;
5747 res = ast_play_and_wait(chan, "vm-no");
5750 if (!res) {
5751 switch (lastnum) {
5752 case 1:
5753 res = ast_play_and_wait(chan, "vm-soobshenie");
5754 break;
5755 case 2:
5756 case 3:
5757 case 4:
5758 res = ast_play_and_wait(chan, "vm-soobsheniya");
5759 break;
5760 default:
5761 res = ast_play_and_wait(chan, "vm-soobsheniy");
5762 break;
5766 return res;
5769 /* UKRAINIAN syntax */
5770 /* in ukrainian the syntax is different so we need the following files
5771 * --------------------------------------------------------
5772 * /digits/ua/1e 'odne'
5773 * vm-nove 'nove'
5774 * vm-stare 'stare'
5777 static int vm_intro_ua(struct ast_channel *chan,struct vm_state *vms)
5779 int res;
5780 int lastnum = 0;
5781 int dcnum;
5783 res = ast_play_and_wait(chan, "vm-youhave");
5784 if (!res && vms->newmessages) {
5785 lastnum = get_lastdigits(vms->newmessages);
5786 dcnum = vms->newmessages - lastnum;
5787 if (dcnum)
5788 res = say_and_wait(chan, dcnum, chan->language);
5789 if (!res && lastnum) {
5790 if (lastnum == 1)
5791 res = ast_play_and_wait(chan, "digits/ua/1e");
5792 else
5793 res = say_and_wait(chan, lastnum, chan->language);
5796 if (!res)
5797 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-nove" : "vm-INBOX");
5799 if (!res && vms->oldmessages)
5800 res = ast_play_and_wait(chan, "vm-and");
5803 if (!res && vms->oldmessages) {
5804 lastnum = get_lastdigits(vms->oldmessages);
5805 dcnum = vms->oldmessages - lastnum;
5806 if (dcnum)
5807 res = say_and_wait(chan, dcnum, chan->language);
5808 if (!res && lastnum) {
5809 if (lastnum == 1)
5810 res = ast_play_and_wait(chan, "digits/ua/1e");
5811 else
5812 res = say_and_wait(chan, lastnum, chan->language);
5815 if (!res)
5816 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-stare" : "vm-Old");
5819 if (!res && !vms->newmessages && !vms->oldmessages) {
5820 lastnum = 0;
5821 res = ast_play_and_wait(chan, "vm-no");
5824 if (!res) {
5825 switch (lastnum) {
5826 case 1:
5827 case 2:
5828 case 3:
5829 case 4:
5830 res = ast_play_and_wait(chan, "vm-message");
5831 break;
5832 default:
5833 res = ast_play_and_wait(chan, "vm-messages");
5834 break;
5838 return res;
5842 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
5844 char prefile[256];
5846 /* Notify the user that the temp greeting is set and give them the option to remove it */
5847 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
5848 if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
5849 if (ast_fileexists(prefile, NULL, NULL) > 0)
5850 ast_play_and_wait(chan, "vm-tempgreetactive");
5853 /* Play voicemail intro - syntax is different for different languages */
5854 if (!strcasecmp(chan->language, "de")) { /* GERMAN syntax */
5855 return vm_intro_de(chan, vms);
5856 } else if (!strcasecmp(chan->language, "es")) { /* SPANISH syntax */
5857 return vm_intro_es(chan, vms);
5858 } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN syntax */
5859 return vm_intro_it(chan, vms);
5860 } else if (!strcasecmp(chan->language, "fr")) { /* FRENCH syntax */
5861 return vm_intro_fr(chan, vms);
5862 } else if (!strcasecmp(chan->language, "nl")) { /* DUTCH syntax */
5863 return vm_intro_nl(chan, vms);
5864 } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE syntax */
5865 return vm_intro_pt(chan, vms);
5866 } else if (!strcasecmp(chan->language, "pt_BR")) { /* BRAZILIAN PORTUGUESE syntax */
5867 return vm_intro_pt_BR(chan, vms);
5868 } else if (!strcasecmp(chan->language, "cz")) { /* CZECH syntax */
5869 return vm_intro_cz(chan, vms);
5870 } else if (!strcasecmp(chan->language, "gr")) { /* GREEK syntax */
5871 return vm_intro_gr(chan, vms);
5872 } else if (!strcasecmp(chan->language, "pl")) { /* POLISH syntax */
5873 return vm_intro_pl(chan, vms);
5874 } else if (!strcasecmp(chan->language, "se")) { /* SWEDISH syntax */
5875 return vm_intro_se(chan, vms);
5876 } else if (!strcasecmp(chan->language, "no")) { /* NORWEGIAN syntax */
5877 return vm_intro_no(chan, vms);
5878 } else if (!strcasecmp(chan->language, "ru")) { /* RUSSIAN syntax */
5879 return vm_intro_ru(chan, vms);
5880 } else if (!strcasecmp(chan->language, "ua")) { /* UKRAINIAN syntax */
5881 return vm_intro_ua(chan, vms);
5882 } else { /* Default to ENGLISH */
5883 return vm_intro_en(chan, vms);
5887 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
5889 int res = 0;
5890 /* Play instructions and wait for new command */
5891 while (!res) {
5892 if (vms->starting) {
5893 if (vms->lastmsg > -1) {
5894 res = ast_play_and_wait(chan, "vm-onefor");
5895 if (!res)
5896 res = vm_play_folder_name(chan, vms->vmbox);
5898 if (!res)
5899 res = ast_play_and_wait(chan, "vm-opts");
5900 } else {
5901 if (vms->curmsg)
5902 res = ast_play_and_wait(chan, "vm-prev");
5903 if (!res && !skipadvanced)
5904 res = ast_play_and_wait(chan, "vm-advopts");
5905 if (!res)
5906 res = ast_play_and_wait(chan, "vm-repeat");
5907 if (!res && (vms->curmsg != vms->lastmsg))
5908 res = ast_play_and_wait(chan, "vm-next");
5909 if (!res) {
5910 if (!vms->deleted[vms->curmsg])
5911 res = ast_play_and_wait(chan, "vm-delete");
5912 else
5913 res = ast_play_and_wait(chan, "vm-undelete");
5914 if (!res)
5915 res = ast_play_and_wait(chan, "vm-toforward");
5916 if (!res)
5917 res = ast_play_and_wait(chan, "vm-savemessage");
5920 if (!res)
5921 res = ast_play_and_wait(chan, "vm-helpexit");
5922 if (!res)
5923 res = ast_waitfordigit(chan, 6000);
5924 if (!res) {
5925 vms->repeats++;
5926 if (vms->repeats > 2) {
5927 res = 't';
5931 return res;
5934 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
5936 int cmd = 0;
5937 int duration = 0;
5938 int tries = 0;
5939 char newpassword[80] = "";
5940 char newpassword2[80] = "";
5941 char prefile[PATH_MAX] = "";
5942 unsigned char buf[256];
5943 int bytes=0;
5945 if (ast_adsi_available(chan)) {
5946 bytes += adsi_logo(buf + bytes);
5947 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
5948 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
5949 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5950 bytes += ast_adsi_voice_mode(buf + bytes, 0);
5951 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5954 /* First, have the user change their password
5955 so they won't get here again */
5956 for (;;) {
5957 newpassword[1] = '\0';
5958 newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
5959 if (cmd == '#')
5960 newpassword[0] = '\0';
5961 if (cmd < 0 || cmd == 't' || cmd == '#')
5962 return cmd;
5963 cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
5964 if (cmd < 0 || cmd == 't' || cmd == '#')
5965 return cmd;
5966 newpassword2[1] = '\0';
5967 newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
5968 if (cmd == '#')
5969 newpassword2[0] = '\0';
5970 if (cmd < 0 || cmd == 't' || cmd == '#')
5971 return cmd;
5972 cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#");
5973 if (cmd < 0 || cmd == 't' || cmd == '#')
5974 return cmd;
5975 if (!strcmp(newpassword, newpassword2))
5976 break;
5977 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
5978 cmd = ast_play_and_wait(chan, "vm-mismatch");
5979 if (++tries == 3)
5980 return -1;
5982 if (ast_strlen_zero(ext_pass_cmd))
5983 vm_change_password(vmu,newpassword);
5984 else
5985 vm_change_password_shell(vmu,newpassword);
5986 if (option_debug > 2)
5987 ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
5988 cmd = ast_play_and_wait(chan,"vm-passchanged");
5990 /* If forcename is set, have the user record their name */
5991 if (ast_test_flag(vmu, VM_FORCENAME)) {
5992 snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
5993 if (ast_fileexists(prefile, NULL, NULL) < 1) {
5994 #ifndef IMAP_STORAGE
5995 cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
5996 #else
5997 cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
5998 #endif
5999 if (cmd < 0 || cmd == 't' || cmd == '#')
6000 return cmd;
6004 /* If forcegreetings is set, have the user record their greetings */
6005 if (ast_test_flag(vmu, VM_FORCEGREET)) {
6006 snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
6007 if (ast_fileexists(prefile, NULL, NULL) < 1) {
6008 #ifndef IMAP_STORAGE
6009 cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6010 #else
6011 cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6012 #endif
6013 if (cmd < 0 || cmd == 't' || cmd == '#')
6014 return cmd;
6017 snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
6018 if (ast_fileexists(prefile, NULL, NULL) < 1) {
6019 #ifndef IMAP_STORAGE
6020 cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6021 #else
6022 cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6023 #endif
6024 if (cmd < 0 || cmd == 't' || cmd == '#')
6025 return cmd;
6029 return cmd;
6032 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
6034 int cmd = 0;
6035 int retries = 0;
6036 int duration = 0;
6037 char newpassword[80] = "";
6038 char newpassword2[80] = "";
6039 char prefile[PATH_MAX] = "";
6040 unsigned char buf[256];
6041 int bytes=0;
6043 if (ast_adsi_available(chan))
6045 bytes += adsi_logo(buf + bytes);
6046 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
6047 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
6048 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6049 bytes += ast_adsi_voice_mode(buf + bytes, 0);
6050 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6052 while ((cmd >= 0) && (cmd != 't')) {
6053 if (cmd)
6054 retries = 0;
6055 switch (cmd) {
6056 case '1':
6057 snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
6058 #ifndef IMAP_STORAGE
6059 cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6060 #else
6061 cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6062 #endif
6063 break;
6064 case '2':
6065 snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
6066 #ifndef IMAP_STORAGE
6067 cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6068 #else
6069 cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6070 #endif
6071 break;
6072 case '3':
6073 snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
6074 #ifndef IMAP_STORAGE
6075 cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6076 #else
6077 cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6078 #endif
6079 break;
6080 case '4':
6081 cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
6082 break;
6083 case '5':
6084 if (vmu->password[0] == '-') {
6085 cmd = ast_play_and_wait(chan, "vm-no");
6086 break;
6088 newpassword[1] = '\0';
6089 newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
6090 if (cmd == '#')
6091 newpassword[0] = '\0';
6092 else {
6093 if (cmd < 0)
6094 break;
6095 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
6096 break;
6099 newpassword2[1] = '\0';
6100 newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
6101 if (cmd == '#')
6102 newpassword2[0] = '\0';
6103 else {
6104 if (cmd < 0)
6105 break;
6107 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
6108 break;
6111 if (strcmp(newpassword, newpassword2)) {
6112 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
6113 cmd = ast_play_and_wait(chan, "vm-mismatch");
6114 break;
6116 if (ast_strlen_zero(ext_pass_cmd))
6117 vm_change_password(vmu,newpassword);
6118 else
6119 vm_change_password_shell(vmu,newpassword);
6120 if (option_debug > 2)
6121 ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
6122 cmd = ast_play_and_wait(chan,"vm-passchanged");
6123 break;
6124 case '*':
6125 cmd = 't';
6126 break;
6127 default:
6128 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
6129 if (ast_fileexists(prefile, NULL, NULL))
6130 cmd = ast_play_and_wait(chan, "vm-tmpexists");
6131 if (!cmd)
6132 cmd = ast_play_and_wait(chan, "vm-options");
6133 if (!cmd)
6134 cmd = ast_waitfordigit(chan,6000);
6135 if (!cmd)
6136 retries++;
6137 if (retries > 3)
6138 cmd = 't';
6141 if (cmd == 't')
6142 cmd = 0;
6143 return cmd;
6146 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
6148 int res;
6149 int cmd = 0;
6150 int retries = 0;
6151 int duration = 0;
6152 char prefile[PATH_MAX] = "";
6153 unsigned char buf[256];
6154 char dest[PATH_MAX];
6155 int bytes = 0;
6157 if (ast_adsi_available(chan)) {
6158 bytes += adsi_logo(buf + bytes);
6159 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
6160 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
6161 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6162 bytes += ast_adsi_voice_mode(buf + bytes, 0);
6163 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6166 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
6167 if ((res = create_dirpath(dest, sizeof(dest), vmu->context, vms->username, "temp"))) {
6168 ast_log(LOG_WARNING, "Failed to create directory (%s).\n", prefile);
6169 return -1;
6171 while ((cmd >= 0) && (cmd != 't')) {
6172 if (cmd)
6173 retries = 0;
6174 RETRIEVE(prefile, -1);
6175 if (ast_fileexists(prefile, NULL, NULL) <= 0) {
6176 #ifndef IMAP_STORAGE
6177 play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6178 #else
6179 play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6180 #endif
6181 cmd = 't';
6182 } else {
6183 switch (cmd) {
6184 case '1':
6185 #ifndef IMAP_STORAGE
6186 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6187 #else
6188 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6189 #endif
6190 break;
6191 case '2':
6192 DELETE(prefile, -1, prefile);
6193 ast_play_and_wait(chan, "vm-tempremoved");
6194 cmd = 't';
6195 break;
6196 case '*':
6197 cmd = 't';
6198 break;
6199 default:
6200 cmd = ast_play_and_wait(chan,
6201 ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
6202 "vm-tempgreeting2" : "vm-tempgreeting");
6203 if (!cmd)
6204 cmd = ast_waitfordigit(chan,6000);
6205 if (!cmd)
6206 retries++;
6207 if (retries > 3)
6208 cmd = 't';
6211 DISPOSE(prefile, -1);
6213 if (cmd == 't')
6214 cmd = 0;
6215 return cmd;
6218 /* GREEK SYNTAX */
6220 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6222 int cmd=0;
6224 if (vms->lastmsg > -1) {
6225 cmd = play_message(chan, vmu, vms);
6226 } else {
6227 cmd = ast_play_and_wait(chan, "vm-youhaveno");
6228 if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
6229 if (!cmd) {
6230 snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
6231 cmd = ast_play_and_wait(chan, vms->fn);
6233 if (!cmd)
6234 cmd = ast_play_and_wait(chan, "vm-messages");
6235 } else {
6236 if (!cmd)
6237 cmd = ast_play_and_wait(chan, "vm-messages");
6238 if (!cmd) {
6239 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6240 cmd = ast_play_and_wait(chan, vms->fn);
6244 return cmd;
6247 /* Default English syntax */
6248 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6250 int cmd=0;
6252 if (vms->lastmsg > -1) {
6253 cmd = play_message(chan, vmu, vms);
6254 } else {
6255 cmd = ast_play_and_wait(chan, "vm-youhave");
6256 if (!cmd)
6257 cmd = ast_play_and_wait(chan, "vm-no");
6258 if (!cmd) {
6259 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6260 cmd = ast_play_and_wait(chan, vms->fn);
6262 if (!cmd)
6263 cmd = ast_play_and_wait(chan, "vm-messages");
6265 return cmd;
6268 /* ITALIAN syntax */
6269 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6271 int cmd=0;
6273 if (vms->lastmsg > -1) {
6274 cmd = play_message(chan, vmu, vms);
6275 } else {
6276 cmd = ast_play_and_wait(chan, "vm-no");
6277 if (!cmd)
6278 cmd = ast_play_and_wait(chan, "vm-message");
6279 if (!cmd) {
6280 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6281 cmd = ast_play_and_wait(chan, vms->fn);
6284 return cmd;
6287 /* SPANISH syntax */
6288 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6290 int cmd=0;
6292 if (vms->lastmsg > -1) {
6293 cmd = play_message(chan, vmu, vms);
6294 } else {
6295 cmd = ast_play_and_wait(chan, "vm-youhaveno");
6296 if (!cmd)
6297 cmd = ast_play_and_wait(chan, "vm-messages");
6298 if (!cmd) {
6299 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6300 cmd = ast_play_and_wait(chan, vms->fn);
6303 return cmd;
6306 /* PORTUGUESE syntax */
6307 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6309 int cmd=0;
6311 if (vms->lastmsg > -1) {
6312 cmd = play_message(chan, vmu, vms);
6313 } else {
6314 cmd = ast_play_and_wait(chan, "vm-no");
6315 if (!cmd) {
6316 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6317 cmd = ast_play_and_wait(chan, vms->fn);
6319 if (!cmd)
6320 cmd = ast_play_and_wait(chan, "vm-messages");
6322 return cmd;
6325 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6327 if (!strcasecmp(chan->language, "es")) { /* SPANISH */
6328 return vm_browse_messages_es(chan, vms, vmu);
6329 } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN */
6330 return vm_browse_messages_it(chan, vms, vmu);
6331 } else if (!strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* PORTUGUESE */
6332 return vm_browse_messages_pt(chan, vms, vmu);
6333 } else if (!strcasecmp(chan->language, "gr")){
6334 return vm_browse_messages_gr(chan, vms, vmu); /* GREEK */
6335 } else { /* Default to English syntax */
6336 return vm_browse_messages_en(chan, vms, vmu);
6340 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
6341 struct ast_vm_user *res_vmu, const char *context, const char *prefix,
6342 int skipuser, int maxlogins, int silent)
6344 int useadsi=0, valid=0, logretries=0;
6345 char password[AST_MAX_EXTENSION]="", *passptr;
6346 struct ast_vm_user vmus, *vmu = NULL;
6348 /* If ADSI is supported, setup login screen */
6349 adsi_begin(chan, &useadsi);
6350 if (!skipuser && useadsi)
6351 adsi_login(chan);
6352 if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
6353 ast_log(LOG_WARNING, "Couldn't stream login file\n");
6354 return -1;
6357 /* Authenticate them and get their mailbox/password */
6359 while (!valid && (logretries < maxlogins)) {
6360 /* Prompt for, and read in the username */
6361 if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
6362 ast_log(LOG_WARNING, "Couldn't read username\n");
6363 return -1;
6365 if (ast_strlen_zero(mailbox)) {
6366 if (chan->cid.cid_num) {
6367 ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
6368 } else {
6369 if (option_verbose > 2)
6370 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
6371 return -1;
6374 if (useadsi)
6375 adsi_password(chan);
6377 if (!ast_strlen_zero(prefix)) {
6378 char fullusername[80] = "";
6379 ast_copy_string(fullusername, prefix, sizeof(fullusername));
6380 strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
6381 ast_copy_string(mailbox, fullusername, mailbox_size);
6384 if (option_debug)
6385 ast_log(LOG_DEBUG, "Before find user for mailbox %s\n",mailbox);
6386 vmu = find_user(&vmus, context, mailbox);
6387 if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
6388 /* saved password is blank, so don't bother asking */
6389 password[0] = '\0';
6390 } else {
6391 if (ast_streamfile(chan, "vm-password", chan->language)) {
6392 ast_log(LOG_WARNING, "Unable to stream password file\n");
6393 return -1;
6395 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
6396 ast_log(LOG_WARNING, "Unable to read password\n");
6397 return -1;
6401 if (vmu) {
6402 passptr = vmu->password;
6403 if (passptr[0] == '-') passptr++;
6405 if (vmu && !strcmp(passptr, password))
6406 valid++;
6407 else {
6408 if (option_verbose > 2)
6409 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
6410 if (!ast_strlen_zero(prefix))
6411 mailbox[0] = '\0';
6413 logretries++;
6414 if (!valid) {
6415 if (skipuser || logretries >= maxlogins) {
6416 if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
6417 ast_log(LOG_WARNING, "Unable to stream incorrect message\n");
6418 return -1;
6420 } else {
6421 if (useadsi)
6422 adsi_login(chan);
6423 if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
6424 ast_log(LOG_WARNING, "Unable to stream incorrect mailbox message\n");
6425 return -1;
6428 if (ast_waitstream(chan, "")) /* Channel is hung up */
6429 return -1;
6432 if (!valid && (logretries >= maxlogins)) {
6433 ast_stopstream(chan);
6434 ast_play_and_wait(chan, "vm-goodbye");
6435 return -1;
6437 if (vmu && !skipuser) {
6438 memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
6440 return 0;
6443 static int vm_execmain(struct ast_channel *chan, void *data)
6445 /* XXX This is, admittedly, some pretty horrendus code. For some
6446 reason it just seemed a lot easier to do with GOTO's. I feel
6447 like I'm back in my GWBASIC days. XXX */
6448 int res=-1;
6449 int cmd=0;
6450 int valid = 0;
6451 struct ast_module_user *u;
6452 char prefixstr[80] ="";
6453 char ext_context[256]="";
6454 int box;
6455 int useadsi = 0;
6456 int skipuser = 0;
6457 struct vm_state vms;
6458 struct ast_vm_user *vmu = NULL, vmus;
6459 char *context=NULL;
6460 int silentexit = 0;
6461 struct ast_flags flags = { 0 };
6462 signed char record_gain = 0;
6463 int play_auto = 0;
6464 int play_folder = 0;
6465 #ifdef IMAP_STORAGE
6466 int deleted = 0;
6467 #endif
6468 u = ast_module_user_add(chan);
6470 /* Add the vm_state to the active list and keep it active */
6471 memset(&vms, 0, sizeof(vms));
6472 vms.lastmsg = -1;
6474 memset(&vmus, 0, sizeof(vmus));
6476 if (chan->_state != AST_STATE_UP) {
6477 if (option_debug)
6478 ast_log(LOG_DEBUG, "Before ast_answer\n");
6479 ast_answer(chan);
6482 if (!ast_strlen_zero(data)) {
6483 char *opts[OPT_ARG_ARRAY_SIZE];
6484 char *parse;
6485 AST_DECLARE_APP_ARGS(args,
6486 AST_APP_ARG(argv0);
6487 AST_APP_ARG(argv1);
6490 parse = ast_strdupa(data);
6492 AST_STANDARD_APP_ARGS(args, parse);
6494 if (args.argc == 2) {
6495 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
6496 ast_module_user_remove(u);
6497 return -1;
6499 if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
6500 int gain;
6501 if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
6502 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
6503 ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
6504 ast_module_user_remove(u);
6505 return -1;
6506 } else {
6507 record_gain = (signed char) gain;
6509 } else {
6510 ast_log(LOG_WARNING, "Invalid Gain level set with option g\n");
6513 if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
6514 play_auto = 1;
6515 if (opts[OPT_ARG_PLAYFOLDER]) {
6516 if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%d", &play_folder) != 1) {
6517 ast_log(LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
6519 } else {
6520 ast_log(LOG_WARNING, "Invalid folder set with option a\n");
6522 if ( play_folder > 9 || play_folder < 0) {
6523 ast_log(LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
6524 play_folder = 0;
6527 } else {
6528 /* old style options parsing */
6529 while (*(args.argv0)) {
6530 if (*(args.argv0) == 's')
6531 ast_set_flag(&flags, OPT_SILENT);
6532 else if (*(args.argv0) == 'p')
6533 ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
6534 else
6535 break;
6536 (args.argv0)++;
6541 valid = ast_test_flag(&flags, OPT_SILENT);
6543 if ((context = strchr(args.argv0, '@')))
6544 *context++ = '\0';
6546 if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
6547 ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
6548 else
6549 ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
6551 if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
6552 skipuser++;
6553 else
6554 valid = 0;
6557 if (!valid)
6558 res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
6560 if (option_debug)
6561 ast_log(LOG_DEBUG, "After vm_authenticate\n");
6562 if (!res) {
6563 valid = 1;
6564 if (!skipuser)
6565 vmu = &vmus;
6566 } else {
6567 res = 0;
6570 /* If ADSI is supported, setup login screen */
6571 adsi_begin(chan, &useadsi);
6573 #ifdef IMAP_STORAGE
6574 vms.interactive = 1;
6575 vms.updated = 1;
6576 vmstate_insert(&vms);
6577 init_vm_state(&vms);
6578 #endif
6579 if (!valid)
6580 goto out;
6582 if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
6583 /* TODO: Handle memory allocation failure */
6585 if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
6586 /* TODO: Handle memory allocation failure */
6589 /* Set language from config to override channel language */
6590 if (!ast_strlen_zero(vmu->language))
6591 ast_string_field_set(chan, language, vmu->language);
6592 create_dirpath(vms.curdir, sizeof(vms.curdir), vmu->context, vms.username, "");
6593 /* Retrieve old and new message counts */
6594 if (option_debug)
6595 ast_log(LOG_DEBUG, "Before open_mailbox\n");
6596 res = open_mailbox(&vms, vmu, 1);
6597 if (res == ERROR_LOCK_PATH)
6598 goto out;
6599 vms.oldmessages = vms.lastmsg + 1;
6600 if (option_debug > 2)
6601 ast_log(LOG_DEBUG, "Number of old messages: %d\n",vms.oldmessages);
6602 /* Start in INBOX */
6603 res = open_mailbox(&vms, vmu, 0);
6604 if (res == ERROR_LOCK_PATH)
6605 goto out;
6606 vms.newmessages = vms.lastmsg + 1;
6607 if (option_debug > 2)
6608 ast_log(LOG_DEBUG, "Number of new messages: %d\n",vms.newmessages);
6610 /* Select proper mailbox FIRST!! */
6611 if (play_auto) {
6612 res = open_mailbox(&vms, vmu, play_folder);
6613 if (res == ERROR_LOCK_PATH)
6614 goto out;
6616 /* If there are no new messages, inform the user and hangup */
6617 if (vms.lastmsg == -1) {
6618 cmd = vm_browse_messages(chan, &vms, vmu);
6619 res = 0;
6620 goto out;
6622 } else {
6623 if (!vms.newmessages && vms.oldmessages) {
6624 /* If we only have old messages start here */
6625 res = open_mailbox(&vms, vmu, 1);
6626 play_folder = 1;
6627 if (res == ERROR_LOCK_PATH)
6628 goto out;
6632 if (useadsi)
6633 adsi_status(chan, &vms);
6634 res = 0;
6636 /* Check to see if this is a new user */
6637 if (!strcasecmp(vmu->mailbox, vmu->password) &&
6638 (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
6639 if (ast_play_and_wait(chan, "vm-newuser") == -1)
6640 ast_log(LOG_WARNING, "Couldn't stream new user file\n");
6641 cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
6642 if ((cmd == 't') || (cmd == '#')) {
6643 /* Timeout */
6644 res = 0;
6645 goto out;
6646 } else if (cmd < 0) {
6647 /* Hangup */
6648 res = -1;
6649 goto out;
6652 #ifdef IMAP_STORAGE
6653 if (option_debug > 2)
6654 ast_log(LOG_DEBUG, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
6655 if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
6656 if (option_debug)
6657 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!!\n");
6658 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
6660 if (option_debug > 2)
6661 ast_log(LOG_DEBUG, "Checking quotas: User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
6662 if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
6663 ast_log(LOG_WARNING, "No more messages possible. User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
6664 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
6666 #endif
6667 if (play_auto) {
6668 cmd = '1';
6669 } else {
6670 cmd = vm_intro(chan, vmu, &vms);
6673 vms.repeats = 0;
6674 vms.starting = 1;
6675 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
6676 /* Run main menu */
6677 switch (cmd) {
6678 case '1':
6679 vms.curmsg = 0;
6680 /* Fall through */
6681 case '5':
6682 cmd = vm_browse_messages(chan, &vms, vmu);
6683 break;
6684 case '2': /* Change folders */
6685 if (useadsi)
6686 adsi_folders(chan, 0, "Change to folder...");
6687 cmd = get_folder2(chan, "vm-changeto", 0);
6688 if (cmd == '#') {
6689 cmd = 0;
6690 } else if (cmd > 0) {
6691 cmd = cmd - '0';
6692 res = close_mailbox(&vms, vmu);
6693 if (res == ERROR_LOCK_PATH)
6694 goto out;
6695 res = open_mailbox(&vms, vmu, cmd);
6696 if (res == ERROR_LOCK_PATH)
6697 goto out;
6698 play_folder = cmd;
6699 cmd = 0;
6701 if (useadsi)
6702 adsi_status2(chan, &vms);
6704 if (!cmd)
6705 cmd = vm_play_folder_name(chan, vms.vmbox);
6707 vms.starting = 1;
6708 break;
6709 case '3': /* Advanced options */
6710 cmd = 0;
6711 vms.repeats = 0;
6712 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
6713 switch (cmd) {
6714 case '1': /* Reply */
6715 if (vms.lastmsg > -1 && !vms.starting) {
6716 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
6717 if (cmd == ERROR_LOCK_PATH) {
6718 res = cmd;
6719 goto out;
6721 } else
6722 cmd = ast_play_and_wait(chan, "vm-sorry");
6723 cmd = 't';
6724 break;
6725 case '2': /* Callback */
6726 if (option_verbose > 2 && !vms.starting)
6727 ast_verbose( VERBOSE_PREFIX_3 "Callback Requested\n");
6728 if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
6729 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
6730 if (cmd == 9) {
6731 silentexit = 1;
6732 goto out;
6733 } else if (cmd == ERROR_LOCK_PATH) {
6734 res = cmd;
6735 goto out;
6738 else
6739 cmd = ast_play_and_wait(chan, "vm-sorry");
6740 cmd = 't';
6741 break;
6742 case '3': /* Envelope */
6743 if (vms.lastmsg > -1 && !vms.starting) {
6744 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
6745 if (cmd == ERROR_LOCK_PATH) {
6746 res = cmd;
6747 goto out;
6749 } else
6750 cmd = ast_play_and_wait(chan, "vm-sorry");
6751 cmd = 't';
6752 break;
6753 case '4': /* Dialout */
6754 if (!ast_strlen_zero(vmu->dialout)) {
6755 cmd = dialout(chan, vmu, NULL, vmu->dialout);
6756 if (cmd == 9) {
6757 silentexit = 1;
6758 goto out;
6761 else
6762 cmd = ast_play_and_wait(chan, "vm-sorry");
6763 cmd = 't';
6764 break;
6766 case '5': /* Leave VoiceMail */
6767 if (ast_test_flag(vmu, VM_SVMAIL)) {
6768 cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain);
6769 if (cmd == ERROR_LOCK_PATH) {
6770 res = cmd;
6771 ast_log(LOG_WARNING, "forward_message failed to lock path.\n");
6772 goto out;
6774 } else
6775 cmd = ast_play_and_wait(chan,"vm-sorry");
6776 cmd='t';
6777 break;
6779 case '*': /* Return to main menu */
6780 cmd = 't';
6781 break;
6783 default:
6784 cmd = 0;
6785 if (!vms.starting) {
6786 cmd = ast_play_and_wait(chan, "vm-toreply");
6788 if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
6789 cmd = ast_play_and_wait(chan, "vm-tocallback");
6791 if (!cmd && !vms.starting) {
6792 cmd = ast_play_and_wait(chan, "vm-tohearenv");
6794 if (!ast_strlen_zero(vmu->dialout) && !cmd) {
6795 cmd = ast_play_and_wait(chan, "vm-tomakecall");
6797 if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
6798 cmd=ast_play_and_wait(chan, "vm-leavemsg");
6799 if (!cmd)
6800 cmd = ast_play_and_wait(chan, "vm-starmain");
6801 if (!cmd)
6802 cmd = ast_waitfordigit(chan,6000);
6803 if (!cmd)
6804 vms.repeats++;
6805 if (vms.repeats > 3)
6806 cmd = 't';
6809 if (cmd == 't') {
6810 cmd = 0;
6811 vms.repeats = 0;
6813 break;
6814 case '4':
6815 if (vms.curmsg > 0) {
6816 vms.curmsg--;
6817 cmd = play_message(chan, vmu, &vms);
6818 } else {
6819 cmd = ast_play_and_wait(chan, "vm-nomore");
6821 break;
6822 case '6':
6823 if (vms.curmsg < vms.lastmsg) {
6824 vms.curmsg++;
6825 cmd = play_message(chan, vmu, &vms);
6826 } else {
6827 cmd = ast_play_and_wait(chan, "vm-nomore");
6829 break;
6830 case '7':
6831 if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
6832 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
6833 if (useadsi)
6834 adsi_delete(chan, &vms);
6835 if (vms.deleted[vms.curmsg]) {
6836 if (play_folder == 0)
6837 vms.newmessages--;
6838 else if (play_folder == 1)
6839 vms.oldmessages--;
6840 cmd = ast_play_and_wait(chan, "vm-deleted");
6842 else {
6843 if (play_folder == 0)
6844 vms.newmessages++;
6845 else if (play_folder == 1)
6846 vms.oldmessages++;
6847 cmd = ast_play_and_wait(chan, "vm-undeleted");
6849 if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
6850 if (vms.curmsg < vms.lastmsg) {
6851 vms.curmsg++;
6852 cmd = play_message(chan, vmu, &vms);
6853 } else {
6854 cmd = ast_play_and_wait(chan, "vm-nomore");
6857 } else /* Delete not valid if we haven't selected a message */
6858 cmd = 0;
6859 #ifdef IMAP_STORAGE
6860 deleted = 1;
6861 #endif
6862 break;
6864 case '8':
6865 if (vms.lastmsg > -1) {
6866 cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain);
6867 if (cmd == ERROR_LOCK_PATH) {
6868 res = cmd;
6869 goto out;
6871 } else
6872 cmd = ast_play_and_wait(chan, "vm-nomore");
6873 break;
6874 case '9':
6875 if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
6876 /* No message selected */
6877 cmd = 0;
6878 break;
6880 if (useadsi)
6881 adsi_folders(chan, 1, "Save to folder...");
6882 cmd = get_folder2(chan, "vm-savefolder", 1);
6883 box = 0; /* Shut up compiler */
6884 if (cmd == '#') {
6885 cmd = 0;
6886 break;
6887 } else if (cmd > 0) {
6888 box = cmd = cmd - '0';
6889 cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
6890 if (cmd == ERROR_LOCK_PATH) {
6891 res = cmd;
6892 goto out;
6893 #ifdef IMAP_STORAGE
6894 } else if (cmd == 10) {
6895 goto out;
6896 #endif
6897 } else if (!cmd) {
6898 vms.deleted[vms.curmsg] = 1;
6899 } else {
6900 vms.deleted[vms.curmsg] = 0;
6901 vms.heard[vms.curmsg] = 0;
6904 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
6905 if (useadsi)
6906 adsi_message(chan, &vms);
6907 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
6908 if (!cmd) {
6909 cmd = ast_play_and_wait(chan, "vm-message");
6910 if (!cmd)
6911 cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
6912 if (!cmd)
6913 cmd = ast_play_and_wait(chan, "vm-savedto");
6914 if (!cmd)
6915 cmd = vm_play_folder_name(chan, vms.fn);
6916 } else {
6917 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
6919 if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
6920 if (vms.curmsg < vms.lastmsg) {
6921 vms.curmsg++;
6922 cmd = play_message(chan, vmu, &vms);
6923 } else {
6924 cmd = ast_play_and_wait(chan, "vm-nomore");
6927 break;
6928 case '*':
6929 if (!vms.starting) {
6930 cmd = ast_play_and_wait(chan, "vm-onefor");
6931 if (!cmd)
6932 cmd = vm_play_folder_name(chan, vms.vmbox);
6933 if (!cmd)
6934 cmd = ast_play_and_wait(chan, "vm-opts");
6935 if (!cmd)
6936 cmd = vm_instructions(chan, &vms, 1);
6937 } else
6938 cmd = 0;
6939 break;
6940 case '0':
6941 cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
6942 if (useadsi)
6943 adsi_status(chan, &vms);
6944 break;
6945 default: /* Nothing */
6946 cmd = vm_instructions(chan, &vms, 0);
6947 break;
6950 if ((cmd == 't') || (cmd == '#')) {
6951 /* Timeout */
6952 res = 0;
6953 } else {
6954 /* Hangup */
6955 res = -1;
6958 out:
6959 if (res > -1) {
6960 ast_stopstream(chan);
6961 adsi_goodbye(chan);
6962 if (valid) {
6963 if (silentexit)
6964 res = ast_play_and_wait(chan, "vm-dialout");
6965 else
6966 res = ast_play_and_wait(chan, "vm-goodbye");
6967 if (res > 0)
6968 res = 0;
6970 if (useadsi)
6971 ast_adsi_unload_session(chan);
6973 if (vmu)
6974 close_mailbox(&vms, vmu);
6975 if (valid) {
6976 snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
6977 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
6978 run_externnotify(vmu->context, vmu->mailbox);
6980 #ifdef IMAP_STORAGE
6981 /* expunge message - use UID Expunge if supported on IMAP server*/
6982 if (option_debug > 2)
6983 ast_log(LOG_DEBUG, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
6984 if (vmu && deleted == 1 && expungeonhangup == 1) {
6985 #ifdef HAVE_IMAP_TK2006
6986 if (LEVELUIDPLUS (vms.mailstream)) {
6987 mail_expunge_full(vms.mailstream,NIL,EX_UID);
6988 } else
6989 #endif
6990 mail_expunge(vms.mailstream);
6992 /* before we delete the state, we should copy pertinent info
6993 * back to the persistent model */
6994 vmstate_delete(&vms);
6995 #endif
6996 if (vmu)
6997 free_user(vmu);
6998 if (vms.deleted)
6999 free(vms.deleted);
7000 if (vms.heard)
7001 free(vms.heard);
7002 ast_module_user_remove(u);
7004 return res;
7007 static int vm_exec(struct ast_channel *chan, void *data)
7009 int res = 0;
7010 struct ast_module_user *u;
7011 char *tmp;
7012 struct leave_vm_options leave_options;
7013 struct ast_flags flags = { 0 };
7014 static int deprecate_warning = 0;
7015 char *opts[OPT_ARG_ARRAY_SIZE];
7016 AST_DECLARE_APP_ARGS(args,
7017 AST_APP_ARG(argv0);
7018 AST_APP_ARG(argv1);
7021 u = ast_module_user_add(chan);
7023 memset(&leave_options, 0, sizeof(leave_options));
7025 if (chan->_state != AST_STATE_UP)
7026 ast_answer(chan);
7028 if (!ast_strlen_zero(data)) {
7029 tmp = ast_strdupa(data);
7030 AST_STANDARD_APP_ARGS(args, tmp);
7031 if (args.argc == 2) {
7032 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
7033 ast_module_user_remove(u);
7034 return -1;
7036 ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_PRIORITY_JUMP);
7037 if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
7038 int gain;
7040 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
7041 ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
7042 ast_module_user_remove(u);
7043 return -1;
7044 } else {
7045 leave_options.record_gain = (signed char) gain;
7048 } else {
7049 /* old style options parsing */
7050 int old = 0;
7051 char *orig_argv0 = args.argv0;
7052 while (*(args.argv0)) {
7053 if (*(args.argv0) == 's') {
7054 old = 1;
7055 ast_set_flag(&leave_options, OPT_SILENT);
7056 } else if (*(args.argv0) == 'b') {
7057 old = 1;
7058 ast_set_flag(&leave_options, OPT_BUSY_GREETING);
7059 } else if (*(args.argv0) == 'u') {
7060 old = 1;
7061 ast_set_flag(&leave_options, OPT_UNAVAIL_GREETING);
7062 } else if (*(args.argv0) == 'j') {
7063 old = 1;
7064 ast_set_flag(&leave_options, OPT_PRIORITY_JUMP);
7065 } else
7066 break;
7067 (args.argv0)++;
7069 if (!deprecate_warning && old) {
7070 deprecate_warning = 1;
7071 ast_log(LOG_WARNING, "Prefixing the mailbox with an option is deprecated ('%s').\n", orig_argv0);
7072 ast_log(LOG_WARNING, "Please move all leading options to the second argument.\n");
7075 } else {
7076 char tmp[256];
7077 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
7078 if (res < 0) {
7079 ast_module_user_remove(u);
7080 return res;
7082 if (ast_strlen_zero(tmp)) {
7083 ast_module_user_remove(u);
7084 return 0;
7086 args.argv0 = ast_strdupa(tmp);
7089 res = leave_voicemail(chan, args.argv0, &leave_options);
7091 if (res == ERROR_LOCK_PATH) {
7092 ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
7093 /*Send the call to n+101 priority, where n is the current priority*/
7094 if (ast_test_flag(&leave_options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
7095 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
7096 ast_log(LOG_WARNING, "Extension %s, priority %d doesn't exist.\n", chan->exten, chan->priority + 101);
7097 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7098 res = 0;
7101 ast_module_user_remove(u);
7103 return res;
7106 static struct ast_vm_user *find_or_create(char *context, char *mbox)
7108 struct ast_vm_user *vmu;
7109 AST_LIST_TRAVERSE(&users, vmu, list) {
7110 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mbox, vmu->mailbox))
7111 break;
7112 if (context && (!strcasecmp(context, vmu->context)) && (!strcasecmp(mbox, vmu->mailbox)))
7113 break;
7116 if (!vmu) {
7117 if ((vmu = ast_calloc(1, sizeof(*vmu)))) {
7118 ast_copy_string(vmu->context, context, sizeof(vmu->context));
7119 ast_copy_string(vmu->mailbox, mbox, sizeof(vmu->mailbox));
7120 AST_LIST_INSERT_TAIL(&users, vmu, list);
7123 return vmu;
7126 static int append_mailbox(char *context, char *mbox, char *data)
7128 /* Assumes lock is already held */
7129 char *tmp;
7130 char *stringp;
7131 char *s;
7132 struct ast_vm_user *vmu;
7134 tmp = ast_strdupa(data);
7136 if ((vmu = find_or_create(context, mbox))) {
7137 populate_defaults(vmu);
7139 stringp = tmp;
7140 if ((s = strsep(&stringp, ",")))
7141 ast_copy_string(vmu->password, s, sizeof(vmu->password));
7142 if (stringp && (s = strsep(&stringp, ",")))
7143 ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
7144 if (stringp && (s = strsep(&stringp, ",")))
7145 ast_copy_string(vmu->email, s, sizeof(vmu->email));
7146 if (stringp && (s = strsep(&stringp, ",")))
7147 ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
7148 if (stringp && (s = strsep(&stringp, ",")))
7149 apply_options(vmu, s);
7151 return 0;
7154 static int vm_box_exists(struct ast_channel *chan, void *data)
7156 struct ast_module_user *u;
7157 struct ast_vm_user svm;
7158 char *context, *box;
7159 int priority_jump = 0;
7160 AST_DECLARE_APP_ARGS(args,
7161 AST_APP_ARG(mbox);
7162 AST_APP_ARG(options);
7165 if (ast_strlen_zero(data)) {
7166 ast_log(LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
7167 return -1;
7170 u = ast_module_user_add(chan);
7172 box = ast_strdupa(data);
7174 AST_STANDARD_APP_ARGS(args, box);
7176 if (args.options) {
7177 if (strchr(args.options, 'j'))
7178 priority_jump = 1;
7181 if ((context = strchr(args.mbox, '@'))) {
7182 *context = '\0';
7183 context++;
7186 if (find_user(&svm, context, args.mbox)) {
7187 pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
7188 if (priority_jump || ast_opt_priority_jumping)
7189 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
7190 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);
7191 } else
7192 pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
7193 ast_module_user_remove(u);
7194 return 0;
7197 static int vmauthenticate(struct ast_channel *chan, void *data)
7199 struct ast_module_user *u;
7200 char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
7201 struct ast_vm_user vmus;
7202 char *options = NULL;
7203 int silent = 0, skipuser = 0;
7204 int res = -1;
7206 u = ast_module_user_add(chan);
7208 if (s) {
7209 s = ast_strdupa(s);
7210 user = strsep(&s, "|");
7211 options = strsep(&s, "|");
7212 if (user) {
7213 s = user;
7214 user = strsep(&s, "@");
7215 context = strsep(&s, "");
7216 if (!ast_strlen_zero(user))
7217 skipuser++;
7218 ast_copy_string(mailbox, user, sizeof(mailbox));
7222 if (options) {
7223 silent = (strchr(options, 's')) != NULL;
7226 if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
7227 pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
7228 pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
7229 ast_play_and_wait(chan, "auth-thankyou");
7230 res = 0;
7233 ast_module_user_remove(u);
7234 return res;
7237 static char voicemail_show_users_help[] =
7238 "Usage: voicemail show users [for <context>]\n"
7239 " Lists all mailboxes currently set up\n";
7241 static char voicemail_show_zones_help[] =
7242 "Usage: voicemail show zones\n"
7243 " Lists zone message formats\n";
7245 static int handle_voicemail_show_users(int fd, int argc, char *argv[])
7247 struct ast_vm_user *vmu;
7248 char *output_format = "%-10s %-5s %-25s %-10s %6s\n";
7250 if ((argc < 3) || (argc > 5) || (argc == 4)) return RESULT_SHOWUSAGE;
7251 else if ((argc == 5) && strcmp(argv[3],"for")) return RESULT_SHOWUSAGE;
7253 AST_LIST_LOCK(&users);
7254 if (!AST_LIST_EMPTY(&users)) {
7255 if (argc == 3)
7256 ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
7257 else {
7258 int count = 0;
7259 AST_LIST_TRAVERSE(&users, vmu, list) {
7260 if (!strcmp(argv[4],vmu->context))
7261 count++;
7263 if (count) {
7264 ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
7265 } else {
7266 ast_cli(fd, "No such voicemail context \"%s\"\n", argv[4]);
7267 AST_LIST_UNLOCK(&users);
7268 return RESULT_FAILURE;
7271 AST_LIST_TRAVERSE(&users, vmu, list) {
7272 int newmsgs = 0, oldmsgs = 0;
7273 char count[12], tmp[256] = "";
7275 if ((argc == 3) || ((argc == 5) && !strcmp(argv[4],vmu->context))) {
7276 snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
7277 inboxcount(tmp, &newmsgs, &oldmsgs);
7278 snprintf(count,sizeof(count),"%d",newmsgs);
7279 ast_cli(fd, output_format, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
7282 } else {
7283 ast_cli(fd, "There are no voicemail users currently defined\n");
7284 AST_LIST_UNLOCK(&users);
7285 return RESULT_FAILURE;
7287 AST_LIST_UNLOCK(&users);
7288 return RESULT_SUCCESS;
7291 static int handle_voicemail_show_zones(int fd, int argc, char *argv[])
7293 struct vm_zone *zone;
7294 char *output_format = "%-15s %-20s %-45s\n";
7295 int res = RESULT_SUCCESS;
7297 if (argc != 3)
7298 return RESULT_SHOWUSAGE;
7300 AST_LIST_LOCK(&zones);
7301 if (!AST_LIST_EMPTY(&zones)) {
7302 ast_cli(fd, output_format, "Zone", "Timezone", "Message Format");
7303 AST_LIST_TRAVERSE(&zones, zone, list) {
7304 ast_cli(fd, output_format, zone->name, zone->timezone, zone->msg_format);
7306 } else {
7307 ast_cli(fd, "There are no voicemail zones currently defined\n");
7308 res = RESULT_FAILURE;
7310 AST_LIST_UNLOCK(&zones);
7312 return res;
7315 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
7317 int which = 0;
7318 int wordlen;
7319 struct ast_vm_user *vmu;
7320 const char *context = "";
7322 /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
7323 if (pos > 4)
7324 return NULL;
7325 if (pos == 3)
7326 return (state == 0) ? ast_strdup("for") : NULL;
7327 wordlen = strlen(word);
7328 AST_LIST_TRAVERSE(&users, vmu, list) {
7329 if (!strncasecmp(word, vmu->context, wordlen)) {
7330 if (context && strcmp(context, vmu->context) && ++which > state)
7331 return ast_strdup(vmu->context);
7332 /* ignore repeated contexts ? */
7333 context = vmu->context;
7336 return NULL;
7339 static struct ast_cli_entry cli_show_voicemail_users_deprecated = {
7340 { "show", "voicemail", "users", NULL },
7341 handle_voicemail_show_users, NULL,
7342 NULL, complete_voicemail_show_users };
7344 static struct ast_cli_entry cli_show_voicemail_zones_deprecated = {
7345 { "show", "voicemail", "zones", NULL },
7346 handle_voicemail_show_zones, NULL,
7347 NULL, NULL };
7349 static struct ast_cli_entry cli_voicemail[] = {
7350 { { "voicemail", "show", "users", NULL },
7351 handle_voicemail_show_users, "List defined voicemail boxes",
7352 voicemail_show_users_help, complete_voicemail_show_users, &cli_show_voicemail_users_deprecated },
7354 { { "voicemail", "show", "zones", NULL },
7355 handle_voicemail_show_zones, "List zone message formats",
7356 voicemail_show_zones_help, NULL, &cli_show_voicemail_zones_deprecated },
7359 static int load_config(void)
7361 struct ast_vm_user *cur;
7362 struct vm_zone *zcur;
7363 struct ast_config *cfg, *ucfg;
7364 char *cat;
7365 struct ast_variable *var;
7366 const char *notifystr = NULL;
7367 const char *smdistr = NULL;
7368 const char *astattach;
7369 const char *astsearch;
7370 const char *astsaycid;
7371 const char *send_voicemail;
7372 #ifdef IMAP_STORAGE
7373 const char *imap_server;
7374 const char *imap_port;
7375 const char *imap_flags;
7376 const char *imap_folder;
7377 const char *auth_user;
7378 const char *auth_password;
7379 const char *expunge_on_hangup;
7380 #endif
7381 const char *astcallop;
7382 const char *astreview;
7383 const char *asttempgreetwarn;
7384 const char *astskipcmd;
7385 const char *asthearenv;
7386 const char *astsaydurationinfo;
7387 const char *astsaydurationminfo;
7388 const char *silencestr;
7389 const char *maxmsgstr;
7390 const char *astdirfwd;
7391 const char *thresholdstr;
7392 const char *fmt;
7393 const char *astemail;
7394 const char *ucontext;
7395 const char *astmailcmd = SENDMAIL;
7396 const char *astforcename;
7397 const char *astforcegreet;
7398 const char *s;
7399 char *q,*stringp;
7400 const char *dialoutcxt = NULL;
7401 const char *callbackcxt = NULL;
7402 const char *exitcxt = NULL;
7403 const char *extpc;
7404 const char *emaildateformatstr;
7405 const char *volgainstr;
7406 int x;
7407 int tmpadsi[4];
7409 cfg = ast_config_load(VOICEMAIL_CONFIG);
7411 AST_LIST_LOCK(&users);
7412 while ((cur = AST_LIST_REMOVE_HEAD(&users, list))) {
7413 ast_set_flag(cur, VM_ALLOCED);
7414 free_user(cur);
7417 AST_LIST_LOCK(&zones);
7418 while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
7419 free_zone(zcur);
7420 AST_LIST_UNLOCK(&zones);
7422 memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
7424 if (cfg) {
7425 /* General settings */
7427 if (!(ucontext = ast_variable_retrieve(cfg, "general", "userscontext")))
7428 ucontext = "default";
7429 ast_copy_string(userscontext, ucontext, sizeof(userscontext));
7430 /* Attach voice message to mail message ? */
7431 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach")))
7432 astattach = "yes";
7433 ast_set2_flag((&globalflags), ast_true(astattach), VM_ATTACH);
7435 if (!(astsearch = ast_variable_retrieve(cfg, "general", "searchcontexts")))
7436 astsearch = "no";
7437 ast_set2_flag((&globalflags), ast_true(astsearch), VM_SEARCH);
7439 volgain = 0.0;
7440 if ((volgainstr = ast_variable_retrieve(cfg, "general", "volgain")))
7441 sscanf(volgainstr, "%lf", &volgain);
7443 #ifdef ODBC_STORAGE
7444 strcpy(odbc_database, "asterisk");
7445 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
7446 ast_copy_string(odbc_database, thresholdstr, sizeof(odbc_database));
7448 strcpy(odbc_table, "voicemessages");
7449 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbctable"))) {
7450 ast_copy_string(odbc_table, thresholdstr, sizeof(odbc_table));
7452 #endif
7453 /* Mail command */
7454 strcpy(mailcmd, SENDMAIL);
7455 if ((astmailcmd = ast_variable_retrieve(cfg, "general", "mailcmd")))
7456 ast_copy_string(mailcmd, astmailcmd, sizeof(mailcmd)); /* User setting */
7458 maxsilence = 0;
7459 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
7460 maxsilence = atoi(silencestr);
7461 if (maxsilence > 0)
7462 maxsilence *= 1000;
7465 if (!(maxmsgstr = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
7466 maxmsg = MAXMSG;
7467 } else {
7468 maxmsg = atoi(maxmsgstr);
7469 if (maxmsg <= 0) {
7470 ast_log(LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", maxmsgstr, MAXMSG);
7471 maxmsg = MAXMSG;
7472 } else if (maxmsg > MAXMSGLIMIT) {
7473 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, maxmsgstr);
7474 maxmsg = MAXMSGLIMIT;
7478 /* Load date format config for voicemail mail */
7479 if ((emaildateformatstr = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
7480 ast_copy_string(emaildateformat, emaildateformatstr, sizeof(emaildateformat));
7483 /* External password changing command */
7484 if ((extpc = ast_variable_retrieve(cfg, "general", "externpass"))) {
7485 ast_copy_string(ext_pass_cmd,extpc,sizeof(ext_pass_cmd));
7487 #ifdef IMAP_STORAGE
7488 /* IMAP server address */
7489 if ((imap_server = ast_variable_retrieve(cfg, "general", "imapserver"))) {
7490 ast_copy_string(imapserver, imap_server, sizeof(imapserver));
7491 } else {
7492 ast_copy_string(imapserver,"localhost", sizeof(imapserver));
7494 /* IMAP server port */
7495 if ((imap_port = ast_variable_retrieve(cfg, "general", "imapport"))) {
7496 ast_copy_string(imapport, imap_port, sizeof(imapport));
7497 } else {
7498 ast_copy_string(imapport,"143", sizeof(imapport));
7500 /* IMAP server flags */
7501 if ((imap_flags = ast_variable_retrieve(cfg, "general", "imapflags"))) {
7502 ast_copy_string(imapflags, imap_flags, sizeof(imapflags));
7504 /* IMAP server master username */
7505 if ((auth_user = ast_variable_retrieve(cfg, "general", "authuser"))) {
7506 ast_copy_string(authuser, auth_user, sizeof(authuser));
7508 /* IMAP server master password */
7509 if ((auth_password = ast_variable_retrieve(cfg, "general", "authpassword"))) {
7510 ast_copy_string(authpassword, auth_password, sizeof(authpassword));
7512 /* Expunge on exit */
7513 if ((expunge_on_hangup = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
7514 if (ast_false(expunge_on_hangup))
7515 expungeonhangup = 0;
7516 else
7517 expungeonhangup = 1;
7518 } else {
7519 expungeonhangup = 1;
7521 /* IMAP voicemail folder */
7522 if ((imap_folder = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
7523 ast_copy_string(imapfolder, imap_folder, sizeof(imapfolder));
7524 } else {
7525 ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
7527 #endif
7528 /* External voicemail notify application */
7530 if ((notifystr = ast_variable_retrieve(cfg, "general", "externnotify"))) {
7531 ast_copy_string(externnotify, notifystr, sizeof(externnotify));
7532 if (option_debug > 2)
7533 ast_log(LOG_DEBUG, "found externnotify: %s\n", externnotify);
7534 if (!strcasecmp(externnotify, "smdi")) {
7535 if (option_debug)
7536 ast_log(LOG_DEBUG, "Using SMDI for external voicemail notification\n");
7537 if ((smdistr = ast_variable_retrieve(cfg, "general", "smdiport"))) {
7538 smdi_iface = ast_smdi_interface_find(smdistr);
7539 } else {
7540 if (option_debug)
7541 ast_log(LOG_DEBUG, "No SMDI interface set, trying default (/dev/ttyS0)\n");
7542 smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
7545 if (!smdi_iface) {
7546 ast_log(LOG_ERROR, "No valid SMDI interface specfied, disabling external voicemail notification\n");
7547 externnotify[0] = '\0';
7550 } else {
7551 externnotify[0] = '\0';
7554 /* Silence treshold */
7555 silencethreshold = 256;
7556 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
7557 silencethreshold = atoi(thresholdstr);
7559 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
7560 astemail = ASTERISK_USERNAME;
7561 ast_copy_string(serveremail, astemail, sizeof(serveremail));
7563 vmmaxmessage = 0;
7564 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
7565 if (sscanf(s, "%d", &x) == 1) {
7566 vmmaxmessage = x;
7567 } else {
7568 ast_log(LOG_WARNING, "Invalid max message time length\n");
7572 vmminmessage = 0;
7573 if ((s = ast_variable_retrieve(cfg, "general", "minmessage"))) {
7574 if (sscanf(s, "%d", &x) == 1) {
7575 vmminmessage = x;
7576 if (maxsilence <= vmminmessage)
7577 ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
7578 } else {
7579 ast_log(LOG_WARNING, "Invalid min message time length\n");
7582 fmt = ast_variable_retrieve(cfg, "general", "format");
7583 if (!fmt)
7584 fmt = "wav";
7585 ast_copy_string(vmfmts, fmt, sizeof(vmfmts));
7587 skipms = 3000;
7588 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
7589 if (sscanf(s, "%d", &x) == 1) {
7590 maxgreet = x;
7591 } else {
7592 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
7596 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
7597 if (sscanf(s, "%d", &x) == 1) {
7598 skipms = x;
7599 } else {
7600 ast_log(LOG_WARNING, "Invalid skipms value\n");
7604 maxlogins = 3;
7605 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
7606 if (sscanf(s, "%d", &x) == 1) {
7607 maxlogins = x;
7608 } else {
7609 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
7613 /* Force new user to record name ? */
7614 if (!(astforcename = ast_variable_retrieve(cfg, "general", "forcename")))
7615 astforcename = "no";
7616 ast_set2_flag((&globalflags), ast_true(astforcename), VM_FORCENAME);
7618 /* Force new user to record greetings ? */
7619 if (!(astforcegreet = ast_variable_retrieve(cfg, "general", "forcegreetings")))
7620 astforcegreet = "no";
7621 ast_set2_flag((&globalflags), ast_true(astforcegreet), VM_FORCEGREET);
7623 if ((s = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))){
7624 if (option_debug > 2)
7625 ast_log(LOG_DEBUG,"VM_CID Internal context string: %s\n",s);
7626 stringp = ast_strdupa(s);
7627 for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
7628 if (!ast_strlen_zero(stringp)) {
7629 q = strsep(&stringp,",");
7630 while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
7631 q++;
7632 ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
7633 if (option_debug > 2)
7634 ast_log(LOG_DEBUG,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
7635 } else {
7636 cidinternalcontexts[x][0] = '\0';
7640 if (!(astreview = ast_variable_retrieve(cfg, "general", "review"))){
7641 if (option_debug)
7642 ast_log(LOG_DEBUG,"VM Review Option disabled globally\n");
7643 astreview = "no";
7645 ast_set2_flag((&globalflags), ast_true(astreview), VM_REVIEW);
7647 /*Temperary greeting reminder */
7648 if (!(asttempgreetwarn = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
7649 if (option_debug)
7650 ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option disabled globally\n");
7651 asttempgreetwarn = "no";
7652 } else {
7653 if (option_debug)
7654 ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option enabled globally\n");
7656 ast_set2_flag((&globalflags), ast_true(asttempgreetwarn), VM_TEMPGREETWARN);
7658 if (!(astcallop = ast_variable_retrieve(cfg, "general", "operator"))){
7659 if (option_debug)
7660 ast_log(LOG_DEBUG,"VM Operator break disabled globally\n");
7661 astcallop = "no";
7663 ast_set2_flag((&globalflags), ast_true(astcallop), VM_OPERATOR);
7665 if (!(astsaycid = ast_variable_retrieve(cfg, "general", "saycid"))) {
7666 if (option_debug)
7667 ast_log(LOG_DEBUG,"VM CID Info before msg disabled globally\n");
7668 astsaycid = "no";
7670 ast_set2_flag((&globalflags), ast_true(astsaycid), VM_SAYCID);
7672 if (!(send_voicemail = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
7673 if (option_debug)
7674 ast_log(LOG_DEBUG,"Send Voicemail msg disabled globally\n");
7675 send_voicemail = "no";
7677 ast_set2_flag((&globalflags), ast_true(send_voicemail), VM_SVMAIL);
7679 if (!(asthearenv = ast_variable_retrieve(cfg, "general", "envelope"))) {
7680 if (option_debug)
7681 ast_log(LOG_DEBUG,"ENVELOPE before msg enabled globally\n");
7682 asthearenv = "yes";
7684 ast_set2_flag((&globalflags), ast_true(asthearenv), VM_ENVELOPE);
7686 if (!(astsaydurationinfo = ast_variable_retrieve(cfg, "general", "sayduration"))) {
7687 if (option_debug)
7688 ast_log(LOG_DEBUG,"Duration info before msg enabled globally\n");
7689 astsaydurationinfo = "yes";
7691 ast_set2_flag((&globalflags), ast_true(astsaydurationinfo), VM_SAYDURATION);
7693 saydurationminfo = 2;
7694 if ((astsaydurationminfo = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
7695 if (sscanf(astsaydurationminfo, "%d", &x) == 1) {
7696 saydurationminfo = x;
7697 } else {
7698 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
7702 if (!(astskipcmd = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
7703 if (option_debug)
7704 ast_log(LOG_DEBUG,"We are not going to skip to the next msg after save/delete\n");
7705 astskipcmd = "no";
7707 ast_set2_flag((&globalflags), ast_true(astskipcmd), VM_SKIPAFTERCMD);
7709 if ((dialoutcxt = ast_variable_retrieve(cfg, "general", "dialout"))) {
7710 ast_copy_string(dialcontext, dialoutcxt, sizeof(dialcontext));
7711 if (option_debug)
7712 ast_log(LOG_DEBUG, "found dialout context: %s\n", dialcontext);
7713 } else {
7714 dialcontext[0] = '\0';
7717 if ((callbackcxt = ast_variable_retrieve(cfg, "general", "callback"))) {
7718 ast_copy_string(callcontext, callbackcxt, sizeof(callcontext));
7719 if (option_debug)
7720 ast_log(LOG_DEBUG, "found callback context: %s\n", callcontext);
7721 } else {
7722 callcontext[0] = '\0';
7725 if ((exitcxt = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
7726 ast_copy_string(exitcontext, exitcxt, sizeof(exitcontext));
7727 if (option_debug)
7728 ast_log(LOG_DEBUG, "found operator context: %s\n", exitcontext);
7729 } else {
7730 exitcontext[0] = '\0';
7733 if (!(astdirfwd = ast_variable_retrieve(cfg, "general", "usedirectory")))
7734 astdirfwd = "no";
7735 ast_set2_flag((&globalflags), ast_true(astdirfwd), VM_DIRECFORWARD);
7736 if ((ucfg = ast_config_load("users.conf"))) {
7737 for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
7738 if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
7739 continue;
7740 if ((cur = find_or_create(userscontext, cat))) {
7741 populate_defaults(cur);
7742 apply_options_full(cur, ast_variable_browse(ucfg, cat));
7743 ast_copy_string(cur->context, userscontext, sizeof(cur->context));
7746 ast_config_destroy(ucfg);
7748 cat = ast_category_browse(cfg, NULL);
7749 while (cat) {
7750 if (strcasecmp(cat, "general")) {
7751 var = ast_variable_browse(cfg, cat);
7752 if (strcasecmp(cat, "zonemessages")) {
7753 /* Process mailboxes in this context */
7754 while (var) {
7755 append_mailbox(cat, var->name, var->value);
7756 var = var->next;
7758 } else {
7759 /* Timezones in this context */
7760 while (var) {
7761 struct vm_zone *z;
7762 if ((z = ast_malloc(sizeof(*z)))) {
7763 char *msg_format, *timezone;
7764 msg_format = ast_strdupa(var->value);
7765 timezone = strsep(&msg_format, "|");
7766 if (msg_format) {
7767 ast_copy_string(z->name, var->name, sizeof(z->name));
7768 ast_copy_string(z->timezone, timezone, sizeof(z->timezone));
7769 ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
7770 AST_LIST_LOCK(&zones);
7771 AST_LIST_INSERT_HEAD(&zones, z, list);
7772 AST_LIST_UNLOCK(&zones);
7773 } else {
7774 ast_log(LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
7775 free(z);
7777 } else {
7778 free(z);
7779 AST_LIST_UNLOCK(&users);
7780 ast_config_destroy(cfg);
7781 return -1;
7783 var = var->next;
7787 cat = ast_category_browse(cfg, cat);
7789 memset(fromstring,0,sizeof(fromstring));
7790 memset(pagerfromstring,0,sizeof(pagerfromstring));
7791 memset(emailtitle,0,sizeof(emailtitle));
7792 strcpy(charset, "ISO-8859-1");
7793 if (emailbody) {
7794 free(emailbody);
7795 emailbody = NULL;
7797 if (emailsubject) {
7798 free(emailsubject);
7799 emailsubject = NULL;
7801 if (pagerbody) {
7802 free(pagerbody);
7803 pagerbody = NULL;
7805 if (pagersubject) {
7806 free(pagersubject);
7807 pagersubject = NULL;
7809 if ((s = ast_variable_retrieve(cfg, "general", "pbxskip")))
7810 ast_set2_flag((&globalflags), ast_true(s), VM_PBXSKIP);
7811 if ((s = ast_variable_retrieve(cfg, "general", "fromstring")))
7812 ast_copy_string(fromstring,s,sizeof(fromstring));
7813 if ((s = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
7814 ast_copy_string(pagerfromstring,s,sizeof(pagerfromstring));
7815 if ((s = ast_variable_retrieve(cfg, "general", "charset")))
7816 ast_copy_string(charset,s,sizeof(charset));
7817 if ((s = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
7818 sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
7819 for (x = 0; x < 4; x++) {
7820 memcpy(&adsifdn[x], &tmpadsi[x], 1);
7823 if ((s = ast_variable_retrieve(cfg, "general", "adsisec"))) {
7824 sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
7825 for (x = 0; x < 4; x++) {
7826 memcpy(&adsisec[x], &tmpadsi[x], 1);
7829 if ((s = ast_variable_retrieve(cfg, "general", "adsiver")))
7830 if (atoi(s)) {
7831 adsiver = atoi(s);
7833 if ((s = ast_variable_retrieve(cfg, "general", "emailtitle"))) {
7834 ast_log(LOG_NOTICE, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
7835 ast_copy_string(emailtitle,s,sizeof(emailtitle));
7837 if ((s = ast_variable_retrieve(cfg, "general", "emailsubject")))
7838 emailsubject = ast_strdup(s);
7839 if ((s = ast_variable_retrieve(cfg, "general", "emailbody"))) {
7840 char *tmpread, *tmpwrite;
7841 emailbody = ast_strdup(s);
7843 /* substitute strings \t and \n into the appropriate characters */
7844 tmpread = tmpwrite = emailbody;
7845 while ((tmpwrite = strchr(tmpread,'\\'))) {
7846 switch (tmpwrite[1]) {
7847 case 'r':
7848 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7849 *tmpwrite = '\r';
7850 break;
7851 case 'n':
7852 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7853 *tmpwrite = '\n';
7854 break;
7855 case 't':
7856 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7857 *tmpwrite = '\t';
7858 break;
7859 default:
7860 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
7862 tmpread = tmpwrite + 1;
7865 if ((s = ast_variable_retrieve(cfg, "general", "pagersubject")))
7866 pagersubject = ast_strdup(s);
7867 if ((s = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
7868 char *tmpread, *tmpwrite;
7869 pagerbody = ast_strdup(s);
7871 /* substitute strings \t and \n into the appropriate characters */
7872 tmpread = tmpwrite = pagerbody;
7873 while ((tmpwrite = strchr(tmpread, '\\'))) {
7874 switch (tmpwrite[1]) {
7875 case 'r':
7876 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7877 *tmpwrite = '\r';
7878 break;
7879 case 'n':
7880 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7881 *tmpwrite = '\n';
7882 break;
7883 case 't':
7884 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7885 *tmpwrite = '\t';
7886 break;
7887 default:
7888 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
7890 tmpread = tmpwrite + 1;
7893 AST_LIST_UNLOCK(&users);
7894 ast_config_destroy(cfg);
7895 return 0;
7896 } else {
7897 AST_LIST_UNLOCK(&users);
7898 ast_log(LOG_WARNING, "Failed to load configuration file.\n");
7899 return 0;
7903 static int reload(void)
7905 return(load_config());
7908 static int unload_module(void)
7910 int res;
7912 res = ast_unregister_application(app);
7913 res |= ast_unregister_application(app2);
7914 res |= ast_unregister_application(app3);
7915 res |= ast_unregister_application(app4);
7916 ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
7917 ast_uninstall_vm_functions();
7919 ast_module_user_hangup_all();
7921 return res;
7924 static int load_module(void)
7926 int res;
7927 char *adsi_loaded = ast_module_helper("", "res_adsi.so", 0, 0, 0, 0);
7928 free(adsi_loaded);
7929 if (!adsi_loaded) {
7930 ast_log(LOG_ERROR, "app_voicemail.so depends upon res_adsi.so\n");
7931 return AST_MODULE_LOAD_DECLINE;
7934 my_umask = umask(0);
7935 umask(my_umask);
7936 res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
7937 res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
7938 res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
7939 res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
7940 if (res)
7941 return(res);
7943 if ((res=load_config())) {
7944 return(res);
7947 ast_cli_register_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
7949 /* compute the location of the voicemail spool directory */
7950 snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
7952 ast_install_vm_functions(has_voicemail, inboxcount, messagecount);
7954 return res;
7957 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
7959 int cmd = 0;
7960 char destination[80] = "";
7961 int retries = 0;
7963 if (!num) {
7964 if (option_verbose > 2)
7965 ast_verbose( VERBOSE_PREFIX_3 "Destination number will be entered manually\n");
7966 while (retries < 3 && cmd != 't') {
7967 destination[1] = '\0';
7968 destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
7969 if (!cmd)
7970 destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
7971 if (!cmd)
7972 destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
7973 if (!cmd) {
7974 cmd = ast_waitfordigit(chan, 6000);
7975 if (cmd)
7976 destination[0] = cmd;
7978 if (!cmd) {
7979 retries++;
7980 } else {
7982 if (cmd < 0)
7983 return 0;
7984 if (cmd == '*') {
7985 if (option_verbose > 2)
7986 ast_verbose( VERBOSE_PREFIX_3 "User hit '*' to cancel outgoing call\n");
7987 return 0;
7989 if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0)
7990 retries++;
7991 else
7992 cmd = 't';
7995 if (retries >= 3) {
7996 return 0;
7999 } else {
8000 if (option_verbose > 2)
8001 ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
8002 ast_copy_string(destination, num, sizeof(destination));
8005 if (!ast_strlen_zero(destination)) {
8006 if (destination[strlen(destination) -1 ] == '*')
8007 return 0;
8008 if (option_verbose > 2)
8009 ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
8010 ast_copy_string(chan->exten, destination, sizeof(chan->exten));
8011 ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
8012 chan->priority = 0;
8013 return 9;
8015 return 0;
8018 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)
8020 int res = 0;
8021 #ifdef IMAP_STORAGE
8022 char origtimeS[256],cidS[256],contextS[256];
8023 char *header_content,*temp;
8024 #endif
8025 char filename[PATH_MAX];
8026 struct ast_config *msg_cfg = NULL;
8027 const char *origtime, *context;
8028 char *cid, *name, *num;
8029 int retries = 0;
8031 vms->starting = 0;
8032 #ifdef IMAP_STORAGE
8033 /* START HERE */
8034 /* get the message info!! */
8035 if (option_debug > 2)
8036 ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
8037 if (vms->msgArray[vms->curmsg] == 0) {
8038 ast_log (LOG_WARNING,"Trying to access unknown message\n");
8039 return -1;
8042 /* This will only work for new messages... */
8043 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
8044 /* empty string means no valid header */
8045 if (ast_strlen_zero(header_content)) {
8046 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
8047 return -1;
8050 /* Get info from headers!! */
8051 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:");
8053 if (temp)
8054 ast_copy_string(cidS,temp, sizeof(cidS));
8055 else
8056 cidS[0] = '\0';
8058 cid = &cidS[0];
8059 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Context:");
8061 if (temp)
8062 ast_copy_string(contextS,temp, sizeof(contextS));
8063 else
8064 contextS[0] = '\0';
8066 context = &contextS[0];
8067 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:");
8069 if (temp)
8070 ast_copy_string(origtimeS,temp, sizeof(origtimeS));
8071 else
8072 origtimeS[0] = '\0';
8074 origtime = &origtimeS[0];
8076 ast_copy_string(filename, "IMAP_STORAGE", sizeof(filename));
8077 #else
8078 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
8080 /* Retrieve info from VM attribute file */
8082 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
8083 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
8084 RETRIEVE(vms->curdir, vms->curmsg);
8085 msg_cfg = ast_config_load(filename);
8086 DISPOSE(vms->curdir, vms->curmsg);
8087 if (!msg_cfg) {
8088 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
8089 return 0;
8092 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
8093 ast_config_destroy(msg_cfg);
8094 return 0;
8097 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
8099 context = ast_variable_retrieve(msg_cfg, "message", "context");
8100 if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
8101 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
8102 #endif
8103 switch (option) {
8104 case 3:
8105 if (!res)
8106 res = play_message_datetime(chan, vmu, origtime, filename);
8107 if (!res)
8108 res = play_message_callerid(chan, vms, cid, context, 0);
8110 res = 't';
8111 break;
8113 case 2: /* Call back */
8115 if (ast_strlen_zero(cid))
8116 break;
8118 ast_callerid_parse(cid, &name, &num);
8119 while ((res > -1) && (res != 't')) {
8120 switch (res) {
8121 case '1':
8122 if (num) {
8123 /* Dial the CID number */
8124 res = dialout(chan, vmu, num, vmu->callback);
8125 if (res) {
8126 ast_config_destroy(msg_cfg);
8127 return 9;
8129 } else {
8130 res = '2';
8132 break;
8134 case '2':
8135 /* Want to enter a different number, can only do this if there's a dialout context for this user */
8136 if (!ast_strlen_zero(vmu->dialout)) {
8137 res = dialout(chan, vmu, NULL, vmu->dialout);
8138 if (res) {
8139 ast_config_destroy(msg_cfg);
8140 return 9;
8142 } else {
8143 if (option_verbose > 2)
8144 ast_verbose( VERBOSE_PREFIX_3 "Caller can not specify callback number - no dialout context available\n");
8145 res = ast_play_and_wait(chan, "vm-sorry");
8147 ast_config_destroy(msg_cfg);
8148 return res;
8149 case '*':
8150 res = 't';
8151 break;
8152 case '3':
8153 case '4':
8154 case '5':
8155 case '6':
8156 case '7':
8157 case '8':
8158 case '9':
8159 case '0':
8161 res = ast_play_and_wait(chan, "vm-sorry");
8162 retries++;
8163 break;
8164 default:
8165 if (num) {
8166 if (option_verbose > 2)
8167 ast_verbose( VERBOSE_PREFIX_3 "Confirm CID number '%s' is number to use for callback\n", num);
8168 res = ast_play_and_wait(chan, "vm-num-i-have");
8169 if (!res)
8170 res = play_message_callerid(chan, vms, num, vmu->context, 1);
8171 if (!res)
8172 res = ast_play_and_wait(chan, "vm-tocallnum");
8173 /* Only prompt for a caller-specified number if there is a dialout context specified */
8174 if (!ast_strlen_zero(vmu->dialout)) {
8175 if (!res)
8176 res = ast_play_and_wait(chan, "vm-calldiffnum");
8178 } else {
8179 res = ast_play_and_wait(chan, "vm-nonumber");
8180 if (!ast_strlen_zero(vmu->dialout)) {
8181 if (!res)
8182 res = ast_play_and_wait(chan, "vm-toenternumber");
8185 if (!res)
8186 res = ast_play_and_wait(chan, "vm-star-cancel");
8187 if (!res)
8188 res = ast_waitfordigit(chan, 6000);
8189 if (!res) {
8190 retries++;
8191 if (retries > 3)
8192 res = 't';
8194 break;
8197 if (res == 't')
8198 res = 0;
8199 else if (res == '*')
8200 res = -1;
8202 break;
8204 case 1: /* Reply */
8205 /* Send reply directly to sender */
8206 if (ast_strlen_zero(cid))
8207 break;
8209 ast_callerid_parse(cid, &name, &num);
8210 if (!num) {
8211 if (option_verbose > 2)
8212 ast_verbose(VERBOSE_PREFIX_3 "No CID number available, no reply sent\n");
8213 if (!res)
8214 res = ast_play_and_wait(chan, "vm-nonumber");
8215 ast_config_destroy(msg_cfg);
8216 return res;
8217 } else {
8218 struct ast_vm_user vmu2;
8219 if (find_user(&vmu2, vmu->context, num)) {
8220 struct leave_vm_options leave_options;
8221 char mailbox[AST_MAX_EXTENSION * 2 + 2];
8222 snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
8224 if (option_verbose > 2)
8225 ast_verbose(VERBOSE_PREFIX_3 "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
8227 memset(&leave_options, 0, sizeof(leave_options));
8228 leave_options.record_gain = record_gain;
8229 res = leave_voicemail(chan, mailbox, &leave_options);
8230 if (!res)
8231 res = 't';
8232 ast_config_destroy(msg_cfg);
8233 return res;
8234 } else {
8235 /* Sender has no mailbox, can't reply */
8236 if (option_verbose > 2)
8237 ast_verbose( VERBOSE_PREFIX_3 "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
8238 ast_play_and_wait(chan, "vm-nobox");
8239 res = 't';
8240 ast_config_destroy(msg_cfg);
8241 return res;
8244 res = 0;
8246 break;
8249 #ifndef IMAP_STORAGE
8250 ast_config_destroy(msg_cfg);
8252 if (!res) {
8253 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
8254 vms->heard[msg] = 1;
8255 res = wait_file(chan, vms, vms->fn);
8257 #endif
8258 return res;
8261 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
8262 int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
8263 signed char record_gain, struct vm_state *vms)
8265 /* Record message & let caller review or re-record it, or set options if applicable */
8266 int res = 0;
8267 int cmd = 0;
8268 int max_attempts = 3;
8269 int attempts = 0;
8270 int recorded = 0;
8271 int message_exists = 0;
8272 signed char zero_gain = 0;
8273 char tempfile[PATH_MAX];
8274 char *acceptdtmf = "#";
8275 char *canceldtmf = "";
8277 /* Note that urgent and private are for flagging messages as such in the future */
8279 /* barf if no pointer passed to store duration in */
8280 if (duration == NULL) {
8281 ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
8282 return -1;
8285 if (!outsidecaller)
8286 snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
8287 else
8288 ast_copy_string(tempfile, recordfile, sizeof(tempfile));
8290 cmd = '3'; /* Want to start by recording */
8292 while ((cmd >= 0) && (cmd != 't')) {
8293 switch (cmd) {
8294 case '1':
8295 if (!message_exists) {
8296 /* In this case, 1 is to record a message */
8297 cmd = '3';
8298 break;
8299 } else {
8300 /* Otherwise 1 is to save the existing message */
8301 if (option_verbose > 2)
8302 ast_verbose(VERBOSE_PREFIX_3 "Saving message as is\n");
8303 if (!outsidecaller)
8304 ast_filerename(tempfile, recordfile, NULL);
8305 ast_stream_and_wait(chan, "vm-msgsaved", chan->language, "");
8306 if (!outsidecaller) {
8307 STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms);
8308 DISPOSE(recordfile, -1);
8310 cmd = 't';
8311 return res;
8313 case '2':
8314 /* Review */
8315 if (option_verbose > 2)
8316 ast_verbose(VERBOSE_PREFIX_3 "Reviewing the message\n");
8317 cmd = ast_stream_and_wait(chan, tempfile, chan->language, AST_DIGIT_ANY);
8318 break;
8319 case '3':
8320 message_exists = 0;
8321 /* Record */
8322 if (recorded == 1) {
8323 if (option_verbose > 2)
8324 ast_verbose(VERBOSE_PREFIX_3 "Re-recording the message\n");
8325 } else {
8326 if (option_verbose > 2)
8327 ast_verbose(VERBOSE_PREFIX_3 "Recording the message\n");
8329 if (recorded && outsidecaller) {
8330 cmd = ast_play_and_wait(chan, INTRO);
8331 cmd = ast_play_and_wait(chan, "beep");
8333 recorded = 1;
8334 /* 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 */
8335 if (record_gain)
8336 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
8337 if (ast_test_flag(vmu, VM_OPERATOR))
8338 canceldtmf = "0";
8339 cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
8340 if (record_gain)
8341 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
8342 if (cmd == -1) {
8343 /* User has hung up, no options to give */
8344 if (!outsidecaller) {
8345 /* user was recording a greeting and they hung up, so let's delete the recording. */
8346 ast_filedelete(tempfile, NULL);
8348 return cmd;
8350 if (cmd == '0') {
8351 break;
8352 } else if (cmd == '*') {
8353 break;
8355 #if 0
8356 else if (vmu->review && (*duration < 5)) {
8357 /* Message is too short */
8358 if (option_verbose > 2)
8359 ast_verbose(VERBOSE_PREFIX_3 "Message too short\n");
8360 cmd = ast_play_and_wait(chan, "vm-tooshort");
8361 cmd = ast_filedelete(tempfile, NULL);
8362 break;
8364 else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
8365 /* Message is all silence */
8366 if (option_verbose > 2)
8367 ast_verbose(VERBOSE_PREFIX_3 "Nothing recorded\n");
8368 cmd = ast_filedelete(tempfile, NULL);
8369 cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
8370 if (!cmd)
8371 cmd = ast_play_and_wait(chan, "vm-speakup");
8372 break;
8374 #endif
8375 else {
8376 /* If all is well, a message exists */
8377 message_exists = 1;
8378 cmd = 0;
8380 break;
8381 case '4':
8382 case '5':
8383 case '6':
8384 case '7':
8385 case '8':
8386 case '9':
8387 case '*':
8388 case '#':
8389 cmd = ast_play_and_wait(chan, "vm-sorry");
8390 break;
8391 #if 0
8392 /* XXX Commented out for the moment because of the dangers of deleting
8393 a message while recording (can put the message numbers out of sync) */
8394 case '*':
8395 /* Cancel recording, delete message, offer to take another message*/
8396 cmd = ast_play_and_wait(chan, "vm-deleted");
8397 cmd = ast_filedelete(tempfile, NULL);
8398 if (outsidecaller) {
8399 res = vm_exec(chan, NULL);
8400 return res;
8402 else
8403 return 1;
8404 #endif
8405 case '0':
8406 if (!ast_test_flag(vmu, VM_OPERATOR)) {
8407 cmd = ast_play_and_wait(chan, "vm-sorry");
8408 break;
8410 if (message_exists || recorded) {
8411 cmd = ast_play_and_wait(chan, "vm-saveoper");
8412 if (!cmd)
8413 cmd = ast_waitfordigit(chan, 3000);
8414 if (cmd == '1') {
8415 ast_play_and_wait(chan, "vm-msgsaved");
8416 cmd = '0';
8417 } else {
8418 ast_play_and_wait(chan, "vm-deleted");
8419 DELETE(recordfile, -1, recordfile);
8420 cmd = '0';
8423 return cmd;
8424 default:
8425 /* If the caller is an ouside caller, and the review option is enabled,
8426 allow them to review the message, but let the owner of the box review
8427 their OGM's */
8428 if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
8429 return cmd;
8430 if (message_exists) {
8431 cmd = ast_play_and_wait(chan, "vm-review");
8433 else {
8434 cmd = ast_play_and_wait(chan, "vm-torerecord");
8435 if (!cmd)
8436 cmd = ast_waitfordigit(chan, 600);
8439 if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
8440 cmd = ast_play_and_wait(chan, "vm-reachoper");
8441 if (!cmd)
8442 cmd = ast_waitfordigit(chan, 600);
8444 #if 0
8445 if (!cmd)
8446 cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
8447 #endif
8448 if (!cmd)
8449 cmd = ast_waitfordigit(chan, 6000);
8450 if (!cmd) {
8451 attempts++;
8453 if (attempts > max_attempts) {
8454 cmd = 't';
8458 if (outsidecaller)
8459 ast_play_and_wait(chan, "vm-goodbye");
8460 if (cmd == 't')
8461 cmd = 0;
8462 return cmd;
8465 #ifdef IMAP_STORAGE
8467 static void write_file(char *filename, char *buffer, unsigned long len)
8469 FILE *output;
8471 output = fopen (filename, "w");
8472 fwrite (buffer, len, 1, output);
8473 fclose (output);
8476 void mm_searched(MAILSTREAM *stream, unsigned long number)
8478 struct vm_state *vms;
8479 char *mailbox;
8480 char *user;
8481 mailbox = stream->mailbox;
8482 user = get_user_by_mailbox(mailbox);
8483 vms = get_vm_state_by_imapuser(user,2);
8484 if (vms) {
8485 if (option_debug > 2)
8486 ast_log (LOG_DEBUG, "saving mailbox message number %lu as message %d. Interactive set to %d\n",number,vms->vmArrayIndex,vms->interactive);
8487 vms->msgArray[vms->vmArrayIndex++] = number;
8488 } else {
8489 ast_log (LOG_ERROR, "No state found.\n");
8494 #if 0 /*No need for this. */
8495 /* MM status report
8496 * Accepts: MAIL stream
8498 static void status(MAILSTREAM *stream)
8500 unsigned long i;
8501 char *s, date[MAILTMPLEN];
8502 THREADER *thr;
8503 AUTHENTICATOR *auth;
8504 rfc822_date (date);
8505 ast_log (LOG_NOTICE,"%s\n",date);
8506 if (stream) {
8507 if (stream->mailbox)
8508 ast_log (LOG_NOTICE," %s mailbox: %s, %lu messages, %lu recent\n",
8509 stream->dtb->name, stream->mailbox, stream->nmsgs,stream->recent);
8510 else
8511 ast_log (LOG_NOTICE,"No mailbox is open on this stream\n");
8512 if (stream->user_flags[0]) {
8513 ast_log (LOG_NOTICE,"Keywords: %s\n", stream->user_flags[0]);
8514 for (i = 1; i < NUSERFLAGS && stream->user_flags[i]; ++i)
8515 ast_log (LOG_NOTICE," %s\n", stream->user_flags[i]);
8517 if (!strcmp (stream->dtb->name, "imap")) {
8518 if (LEVELIMAP4rev1 (stream))
8519 s = "IMAP4rev1 (RFC 3501)";
8520 else if (LEVEL1730 (stream))
8521 s = "IMAP4 (RFC 1730)";
8522 else if (LEVELIMAP2bis (stream))
8523 s = "IMAP2bis";
8524 else if (LEVEL1176 (stream))
8525 s = "IMAP2 (RFC 1176)";
8526 else
8527 s = "IMAP2 (RFC 1064)";
8528 ast_log (LOG_NOTICE,"%s server %s\n", s, imap_host (stream));
8529 if (LEVELIMAP4 (stream)) {
8530 if ((i = (imap_cap(stream)->auth))) {
8531 s = "";
8532 ast_log (LOG_NOTICE,"Mutually-supported SASL mechanisms:\n");
8533 while ((auth = mail_lookup_auth (find_rightmost_bit (&i) + 1))) {
8534 ast_log (LOG_NOTICE," %s\n", auth->name);
8535 if (!strcmp (auth->name, "PLAIN"))
8536 s = "\n [LOGIN will not be listed here if PLAIN is supported]\n";
8538 ast_log (LOG_NOTICE,s);
8540 ast_log (LOG_NOTICE,"Supported standard extensions:\n");
8541 if (LEVELACL (stream))
8542 ast_log (LOG_NOTICE," Access Control lists (RFC 2086)\n");
8543 if (LEVELQUOTA (stream))
8544 ast_log (LOG_NOTICE," Quotas (RFC 2087)\n");
8545 if (LEVELLITERALPLUS (stream))
8546 ast_log (LOG_NOTICE," Non-synchronizing literals (RFC 2088)\n");
8547 if (LEVELIDLE (stream))
8548 ast_log (LOG_NOTICE," IDLE unsolicited update (RFC 2177)\n");
8549 if (LEVELMBX_REF (stream))
8550 ast_log (LOG_NOTICE," Mailbox referrals (RFC 2193)\n");
8551 if (LEVELLOG_REF (stream))
8552 ast_log (LOG_NOTICE," Login referrals (RFC 2221)\n");
8553 if (LEVELANONYMOUS (stream))
8554 ast_log (LOG_NOTICE," Anonymous access (RFC 2245)\n");
8555 if (LEVELNAMESPACE (stream))
8556 ast_log (LOG_NOTICE," Multiple namespaces (RFC 2342)\n");
8557 if (LEVELUIDPLUS (stream))
8558 ast_log (LOG_NOTICE," Extended UID behavior (RFC 2359)\n");
8559 if (LEVELSTARTTLS (stream))
8560 ast_log (LOG_NOTICE," Transport Layer Security (RFC 2595)\n");
8561 if (LEVELLOGINDISABLED (stream))
8562 ast_log (LOG_NOTICE," LOGIN command disabled (RFC 2595)\n");
8563 if (LEVELID (stream))
8564 ast_log (LOG_NOTICE," Implementation identity negotiation (RFC 2971)\n");
8565 if (LEVELCHILDREN (stream))
8566 ast_log (LOG_NOTICE," LIST children announcement (RFC 3348)\n");
8567 if (LEVELMULTIAPPEND (stream))
8568 ast_log (LOG_NOTICE," Atomic multiple APPEND (RFC 3502)\n");
8569 if (LEVELBINARY (stream))
8570 ast_log (LOG_NOTICE," Binary body content (RFC 3516)\n");
8571 ast_log (LOG_NOTICE,"Supported draft extensions:\n");
8572 if (LEVELUNSELECT (stream))
8573 ast_log (LOG_NOTICE," Mailbox unselect\n");
8574 if (LEVELSASLIR (stream))
8575 ast_log (LOG_NOTICE," SASL initial client response\n");
8576 if (LEVELSORT (stream))
8577 ast_log (LOG_NOTICE," Server-based sorting\n");
8578 if (LEVELTHREAD (stream)) {
8579 ast_log (LOG_NOTICE," Server-based threading:\n");
8580 for (thr = imap_cap(stream)->threader; thr; thr = thr->next)
8581 ast_log (LOG_NOTICE," %s\n", thr->name);
8583 if (LEVELSCAN (stream))
8584 ast_log (LOG_NOTICE," Mailbox text scan\n");
8585 if ((i = imap_cap(stream)->extlevel)) {
8586 ast_log (LOG_NOTICE,"Supported BODYSTRUCTURE extensions:\n");
8587 switch (i) {
8588 case BODYEXTLOC:
8589 ast_log (LOG_NOTICE," location\n");
8590 case BODYEXTLANG:
8591 ast_log (LOG_NOTICE," language\n");
8592 case BODYEXTDSP:
8593 ast_log (LOG_NOTICE," disposition\n");
8594 case BODYEXTMD5:
8595 ast_log (LOG_NOTICE," MD5\n");
8598 }else
8599 ast_log (LOG_NOTICE,"\n");
8603 #endif
8605 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
8607 struct ast_variable *var;
8608 struct ast_vm_user *vmu;
8610 vmu = ast_calloc(1, sizeof *vmu);
8611 if (!vmu)
8612 return NULL;
8613 ast_set_flag(vmu, VM_ALLOCED);
8614 populate_defaults(vmu);
8616 var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
8617 if (var) {
8618 apply_options_full(vmu, var);
8619 ast_variables_destroy(var);
8620 return vmu;
8621 } else {
8622 free(vmu);
8623 return NULL;
8627 /* Interfaces to C-client */
8629 void mm_exists(MAILSTREAM * stream, unsigned long number)
8631 /* mail_ping will callback here if new mail! */
8632 if (option_debug > 3)
8633 ast_log (LOG_DEBUG, "Entering EXISTS callback for message %ld\n", number);
8634 if (number == 0) return;
8635 set_update(stream);
8639 void mm_expunged(MAILSTREAM * stream, unsigned long number)
8641 /* mail_ping will callback here if expunged mail! */
8642 if (option_debug > 3)
8643 ast_log (LOG_DEBUG, "Entering EXPUNGE callback for message %ld\n", number);
8644 if (number == 0) return;
8645 set_update(stream);
8649 void mm_flags(MAILSTREAM * stream, unsigned long number)
8651 /* mail_ping will callback here if read mail! */
8652 if (option_debug > 3)
8653 ast_log (LOG_DEBUG, "Entering FLAGS callback for message %ld\n", number);
8654 if (number == 0) return;
8655 set_update(stream);
8659 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
8661 mm_log (string, errflg);
8665 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
8667 if (delimiter == '\0') {
8668 delimiter = delim;
8670 if (option_debug > 4) {
8671 ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
8672 if (attributes & LATT_NOINFERIORS)
8673 ast_log(LOG_DEBUG, "no inferiors\n");
8674 if (attributes & LATT_NOSELECT)
8675 ast_log(LOG_DEBUG, "no select\n");
8676 if (attributes & LATT_MARKED)
8677 ast_log(LOG_DEBUG, "marked\n");
8678 if (attributes & LATT_UNMARKED)
8679 ast_log(LOG_DEBUG, "unmarked\n");
8684 void mm_lsub(MAILSTREAM * stream, int delimiter, char *mailbox, long attributes)
8686 if (option_debug > 4) {
8687 ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delimiter, mailbox);
8688 if (attributes & LATT_NOINFERIORS)
8689 ast_log(LOG_DEBUG, "no inferiors\n");
8690 if (attributes & LATT_NOSELECT)
8691 ast_log(LOG_DEBUG, "no select\n");
8692 if (attributes & LATT_MARKED)
8693 ast_log(LOG_DEBUG, "marked\n");
8694 if (attributes & LATT_UNMARKED)
8695 ast_log(LOG_DEBUG, "unmarked\n");
8700 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
8702 ast_log (LOG_NOTICE," Mailbox %s", mailbox);
8703 if (status->flags & SA_MESSAGES)
8704 ast_log (LOG_NOTICE,", %lu messages", status->messages);
8705 if (status->flags & SA_RECENT)
8706 ast_log (LOG_NOTICE,", %lu recent", status->recent);
8707 if (status->flags & SA_UNSEEN)
8708 ast_log (LOG_NOTICE,", %lu unseen", status->unseen);
8709 if (status->flags & SA_UIDVALIDITY)
8710 ast_log (LOG_NOTICE,", %lu UID validity", status->uidvalidity);
8711 if (status->flags & SA_UIDNEXT)
8712 ast_log (LOG_NOTICE,", %lu next UID", status->uidnext);
8713 ast_log (LOG_NOTICE,"\n");
8717 void mm_log(char *string, long errflg)
8719 switch ((short) errflg) {
8720 case NIL:
8721 if (option_debug)
8722 ast_log(LOG_DEBUG,"IMAP Info: %s\n", string);
8723 break;
8724 case PARSE:
8725 case WARN:
8726 ast_log (LOG_WARNING,"IMAP Warning: %s\n", string);
8727 break;
8728 case ERROR:
8729 ast_log (LOG_ERROR,"IMAP Error: %s\n", string);
8730 break;
8735 void mm_dlog(char *string)
8737 ast_log (LOG_NOTICE, "%s\n", string);
8741 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
8743 struct ast_vm_user *vmu;
8745 if (option_debug > 3)
8746 ast_log(LOG_DEBUG, "Entering callback mm_login\n");
8748 ast_copy_string(user, mb->user, MAILTMPLEN);
8750 /* We should only do this when necessary */
8751 if (!ast_strlen_zero(authpassword)) {
8752 ast_copy_string(pwd, authpassword, MAILTMPLEN);
8753 } else {
8754 AST_LIST_TRAVERSE(&users, vmu, list) {
8755 if (!strcasecmp(mb->user, vmu->imapuser)) {
8756 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
8757 break;
8760 if (!vmu) {
8761 if ((vmu = find_user_realtime_imapuser(mb->user))) {
8762 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
8763 free_user(vmu);
8770 void mm_critical(MAILSTREAM * stream)
8775 void mm_nocritical(MAILSTREAM * stream)
8780 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
8782 kill (getpid (), SIGSTOP);
8783 return NIL;
8787 void mm_fatal(char *string)
8789 ast_log(LOG_ERROR,"IMAP access FATAL error: %s\n", string);
8792 /* C-client callback to handle quota */
8793 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
8795 struct vm_state *vms;
8796 char *mailbox;
8797 char *user;
8798 unsigned long usage = 0;
8799 unsigned long limit = 0;
8801 while (pquota) {
8802 usage = pquota->usage;
8803 limit = pquota->limit;
8804 pquota = pquota->next;
8807 mailbox = stream->mailbox;
8808 user = get_user_by_mailbox(mailbox);
8809 vms = get_vm_state_by_imapuser(user,2);
8810 if (vms) {
8811 if (option_debug > 2)
8812 ast_log (LOG_DEBUG, "User %s usage is %lu, limit is %lu\n",user,usage,limit);
8813 vms->quota_usage = usage;
8814 vms->quota_limit = limit;
8815 } else {
8816 ast_log (LOG_ERROR, "No state found.\n");
8820 static char *get_header_by_tag(char *header, char *tag)
8822 char *start;
8823 int taglen;
8824 char *eol_pnt;
8826 if (!header || !tag)
8827 return NULL;
8829 taglen = strlen(tag) + 1;
8830 if (taglen < 1)
8831 return NULL;
8833 start = strstr(header, tag);
8834 if (!start)
8835 return NULL;
8837 ast_mutex_lock(&imaptemp_lock);
8838 ast_copy_string(imaptemp, start+taglen, sizeof(imaptemp));
8839 ast_mutex_unlock(&imaptemp_lock);
8840 if ((eol_pnt = strchr(imaptemp,'\r')) || (eol_pnt = strchr(imaptemp,'\n')))
8841 *eol_pnt = '\0';
8842 return imaptemp;
8845 static char *get_user_by_mailbox(char *mailbox)
8847 char *start, *quote;
8848 char *eol_pnt;
8850 if (!mailbox)
8851 return NULL;
8853 start = strstr(mailbox,"/user=");
8854 if (!start)
8855 return NULL;
8857 ast_mutex_lock(&imaptemp_lock);
8858 ast_copy_string(imaptemp, start+6, sizeof(imaptemp));
8859 ast_mutex_unlock(&imaptemp_lock);
8861 quote = strchr(imaptemp,'\"');
8862 if (!quote) { /* if username is not in quotes */
8863 eol_pnt = strchr(imaptemp,'/');
8864 if (!eol_pnt) {
8865 eol_pnt = strchr(imaptemp,'}');
8867 *eol_pnt = '\0';
8868 return imaptemp;
8869 } else {
8870 eol_pnt = strchr(imaptemp+1,'\"');
8871 *eol_pnt = '\0';
8872 return imaptemp+1;
8876 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
8878 struct vmstate *vlist = NULL;
8880 ast_mutex_lock(&vmstate_lock);
8881 vlist = vmstates;
8882 while (vlist) {
8883 if (vlist->vms) {
8884 if (vlist->vms->imapuser) {
8885 if (!strcmp(vlist->vms->imapuser,user)) {
8886 if (interactive == 2) {
8887 ast_mutex_unlock(&vmstate_lock);
8888 return vlist->vms;
8889 } else if (vlist->vms->interactive == interactive) {
8890 ast_mutex_unlock(&vmstate_lock);
8891 return vlist->vms;
8894 } else {
8895 if (option_debug > 2)
8896 ast_log(LOG_DEBUG, " error: imapuser is NULL for %s\n",user);
8898 } else {
8899 if (option_debug > 2)
8900 ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",user);
8902 vlist = vlist->next;
8904 ast_mutex_unlock(&vmstate_lock);
8905 if (option_debug > 2)
8906 ast_log(LOG_DEBUG, "%s not found in vmstates\n",user);
8907 return NULL;
8910 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive)
8912 struct vmstate *vlist = NULL;
8914 ast_mutex_lock(&vmstate_lock);
8915 vlist = vmstates;
8916 if (option_debug > 2)
8917 ast_log(LOG_DEBUG, "Mailbox set to %s\n",mailbox);
8918 while (vlist) {
8919 if (vlist->vms) {
8920 if (vlist->vms->username) {
8921 if (option_debug > 2)
8922 ast_log(LOG_DEBUG, " comparing mailbox %s (i=%d) to vmstate mailbox %s (i=%d)\n",mailbox,interactive,vlist->vms->username,vlist->vms->interactive);
8923 if (!strcmp(vlist->vms->username,mailbox) && vlist->vms->interactive == interactive) {
8924 if (option_debug > 2)
8925 ast_log(LOG_DEBUG, " Found it!\n");
8926 ast_mutex_unlock(&vmstate_lock);
8927 return vlist->vms;
8929 } else {
8930 if (option_debug > 2)
8931 ast_log(LOG_DEBUG, " error: username is NULL for %s\n",mailbox);
8933 } else {
8934 if (option_debug > 2)
8935 ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",mailbox);
8937 vlist = vlist->next;
8939 ast_mutex_unlock(&vmstate_lock);
8940 if (option_debug > 2)
8941 ast_log(LOG_DEBUG, "%s not found in vmstates\n",mailbox);
8942 return NULL;
8945 static void vmstate_insert(struct vm_state *vms)
8947 struct vmstate *v;
8948 struct vm_state *altvms;
8950 /* If interactive, it probably already exists, and we should
8951 use the one we already have since it is more up to date.
8952 We can compare the username to find the duplicate */
8953 if (vms->interactive == 1) {
8954 altvms = get_vm_state_by_mailbox(vms->username,0);
8955 if (altvms) {
8956 if (option_debug > 2)
8957 ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
8958 vms->newmessages = altvms->newmessages;
8959 vms->oldmessages = altvms->oldmessages;
8960 if (option_debug > 2)
8961 ast_log(LOG_DEBUG, "check_msgArray before memcpy\n");
8962 check_msgArray(vms);
8963 /* memcpy(vms->msgArray, altvms->msgArray, sizeof(long)*256); */
8964 copy_msgArray(vms, altvms);
8965 if (option_debug > 2)
8966 ast_log(LOG_DEBUG, "check_msgArray after memcpy\n");
8967 check_msgArray(vms);
8968 vms->vmArrayIndex = altvms->vmArrayIndex;
8969 vms->lastmsg = altvms->lastmsg;
8970 vms->curmsg = altvms->curmsg;
8971 /* get a pointer to the persistent store */
8972 vms->persist_vms = altvms;
8973 /* Reuse the mailstream? */
8974 vms->mailstream = altvms->mailstream;
8975 /* vms->mailstream = NIL; */
8979 v = (struct vmstate *)malloc(sizeof(struct vmstate));
8980 if (!v) {
8981 ast_log(LOG_ERROR, "Out of memory\n");
8983 if (option_debug > 2)
8984 ast_log(LOG_DEBUG, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
8985 ast_mutex_lock(&vmstate_lock);
8986 v->vms = vms;
8987 v->next = vmstates;
8988 vmstates = v;
8989 ast_mutex_unlock(&vmstate_lock);
8992 static void vmstate_delete(struct vm_state *vms)
8994 struct vmstate *vc, *vf = NULL, *vl = NULL;
8995 struct vm_state *altvms;
8997 /* If interactive, we should copy pertainent info
8998 back to the persistent state (to make update immediate) */
8999 if (vms->interactive == 1) {
9000 altvms = vms->persist_vms;
9001 if (altvms) {
9002 if (option_debug > 2)
9003 ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
9004 altvms->newmessages = vms->newmessages;
9005 altvms->oldmessages = vms->oldmessages;
9006 altvms->updated = 1;
9010 ast_mutex_lock(&vmstate_lock);
9011 vc = vmstates;
9012 if (option_debug > 2)
9013 ast_log(LOG_DEBUG, "Removing vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
9014 while (vc) {
9015 if (vc->vms == vms) {
9016 vf = vc;
9017 if (vl)
9018 vl->next = vc->next;
9019 else
9020 vmstates = vc->next;
9021 break;
9023 vl = vc;
9024 vc = vc->next;
9026 if (!vf) {
9027 ast_log(LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n",vms->imapuser,vms->username);
9028 } else {
9029 free(vf);
9031 ast_mutex_unlock(&vmstate_lock);
9034 static void set_update(MAILSTREAM * stream)
9036 struct vm_state *vms;
9037 char *mailbox;
9038 char *user;
9040 mailbox = stream->mailbox;
9041 user = get_user_by_mailbox(mailbox);
9042 vms = get_vm_state_by_imapuser(user, 0);
9043 if (vms) {
9044 if (option_debug > 2)
9045 ast_log (LOG_DEBUG, "User %s mailbox set for update.\n",user);
9046 vms->updated = 1; /* set updated flag since mailbox changed */
9047 } else {
9048 if (option_debug > 2)
9049 ast_log (LOG_WARNING, "User %s mailbox not found for update.\n",user);
9053 static void init_vm_state(struct vm_state *vms)
9055 int x;
9056 vms->vmArrayIndex = 0;
9057 for (x = 0; x < 256; x++) {
9058 vms->msgArray[x] = 0;
9062 static void check_msgArray(struct vm_state *vms)
9064 int x;
9065 for (x = 0; x<256; x++) {
9066 if (vms->msgArray[x]!=0) {
9067 if (option_debug)
9068 ast_log (LOG_DEBUG, "Item %d set to %ld\n",x,vms->msgArray[x]);
9073 static void copy_msgArray(struct vm_state *dst, struct vm_state *src)
9075 int x;
9076 for (x = 0; x<256; x++) {
9077 dst->msgArray[x] = src->msgArray[x];
9081 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format)
9083 char *body_content;
9084 char *body_decoded;
9085 unsigned long len;
9086 unsigned long newlen;
9087 char filename[256];
9089 if (!body || body == NIL)
9090 return -1;
9091 body_content = mail_fetchbody (vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
9092 if (body_content != NIL) {
9093 snprintf(filename, sizeof(filename), "%s.%s", vms->fn, format);
9094 /* ast_log (LOG_DEBUG,body_content); */
9095 body_decoded = rfc822_base64 ((unsigned char *)body_content, len, &newlen);
9096 write_file (filename, (char *) body_decoded, newlen);
9098 return 0;
9101 /* get delimiter via mm_list callback */
9102 static void get_mailbox_delimiter(MAILSTREAM *stream) {
9103 char tmp[50];
9104 snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
9105 mail_list(stream, tmp, "*");
9108 /* Check Quota for user */
9109 static void check_quota(struct vm_state *vms, char *mailbox) {
9110 mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
9111 if (option_debug > 2)
9112 ast_log(LOG_DEBUG, "Mailbox name set to: %s, about to check quotas\n", mailbox);
9113 if (vms && vms->mailstream != NULL) {
9114 imap_getquotaroot(vms->mailstream, mailbox);
9115 } else {
9116 ast_log(LOG_WARNING,"Mailstream not available for mailbox: %s\n",mailbox);
9120 #endif /* IMAP_STORAGE */
9122 /* This is a workaround so that menuselect displays a proper description
9123 * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
9126 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
9127 .load = load_module,
9128 .unload = unload_module,
9129 .reload = reload,