(closes issue #12846)
[asterisk-bristuff.git] / apps / app_voicemail.c
blob57f2fc54dd6bf900a2833b022f201c3af53c1ae6
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 ast_mutex_t lock;
376 int updated; /* decremented on each mail check until 1 -allows delay */
377 long msgArray[256];
378 MAILSTREAM *mailstream;
379 int vmArrayIndex;
380 char imapuser[80]; /* IMAP server login */
381 int interactive;
382 unsigned int quota_limit;
383 unsigned int quota_usage;
384 struct vm_state *persist_vms;
385 #endif
387 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
388 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
389 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
390 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
391 signed char record_gain, struct vm_state *vms);
392 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
393 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
394 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
395 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap);
396 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
397 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
398 #endif
399 static void apply_options(struct ast_vm_user *vmu, const char *options);
401 #ifdef ODBC_STORAGE
402 static char odbc_database[80];
403 static char odbc_table[80];
404 #define RETRIEVE(a,b) retrieve_file(a,b)
405 #define DISPOSE(a,b) remove_file(a,b)
406 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
407 #define EXISTS(a,b,c,d) (message_exists(a,b))
408 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
409 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
410 #define DELETE(a,b,c) (delete_file(a,b))
411 #else
412 #ifdef IMAP_STORAGE
413 #define RETRIEVE(a,b)
414 #define DISPOSE(a,b)
415 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
416 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
417 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
418 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
419 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
420 #define DELETE(a,b,c) (vm_delete(c))
421 #else
422 #define RETRIEVE(a,b)
423 #define DISPOSE(a,b)
424 #define STORE(a,b,c,d,e,f,g,h,i)
425 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
426 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
427 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
428 #define DELETE(a,b,c) (vm_delete(c))
429 #endif
430 #endif
432 static char VM_SPOOL_DIR[PATH_MAX];
434 static char ext_pass_cmd[128];
436 int my_umask;
438 #if ODBC_STORAGE
439 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
440 #elif IMAP_STORAGE
441 #define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
442 #else
443 #define tdesc "Comedian Mail (Voicemail System)"
444 #endif
446 static char userscontext[AST_MAX_EXTENSION] = "default";
448 static char *addesc = "Comedian Mail";
450 static char *synopsis_vm =
451 "Leave a Voicemail message";
453 static char *descrip_vm =
454 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
455 "application allows the calling party to leave a message for the specified\n"
456 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
457 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
458 "specified mailbox does not exist.\n"
459 " The Voicemail application will exit if any of the following DTMF digits are\n"
460 "received:\n"
461 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
462 " * - Jump to the 'a' extension in the current dialplan context.\n"
463 " This application will set the following channel variable upon completion:\n"
464 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
465 " application. The possible values are:\n"
466 " SUCCESS | USEREXIT | FAILED\n\n"
467 " Options:\n"
468 " b - Play the 'busy' greeting to the calling party.\n"
469 " g(#) - Use the specified amount of gain when recording the voicemail\n"
470 " message. The units are whole-number decibels (dB).\n"
471 " Only works on supported technologies, which is Zap only.\n"
472 " s - Skip the playback of instructions for leaving a message to the\n"
473 " calling party.\n"
474 " u - Play the 'unavailable' greeting.\n"
475 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
476 " error occurs.\n";
478 static char *synopsis_vmain =
479 "Check Voicemail messages";
481 static char *descrip_vmain =
482 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
483 "calling party to check voicemail messages. A specific mailbox, and optional\n"
484 "corresponding context, may be specified. If a mailbox is not provided, the\n"
485 "calling party will be prompted to enter one. If a context is not specified,\n"
486 "the 'default' context will be used.\n\n"
487 " Options:\n"
488 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
489 " is entered by the caller.\n"
490 " g(#) - Use the specified amount of gain when recording a voicemail\n"
491 " message. The units are whole-number decibels (dB).\n"
492 " s - Skip checking the passcode for the mailbox.\n"
493 " a(#) - Skip folder prompt and go directly to folder specified.\n"
494 " Defaults to INBOX\n";
496 static char *synopsis_vm_box_exists =
497 "Check to see if Voicemail mailbox exists";
499 static char *descrip_vm_box_exists =
500 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
501 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
502 "will be used.\n"
503 " This application will set the following channel variable upon completion:\n"
504 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
505 " MailboxExists application. Possible values include:\n"
506 " SUCCESS | FAILED\n\n"
507 " Options:\n"
508 " j - Jump to priority n+101 if the mailbox is found.\n";
510 static char *synopsis_vmauthenticate =
511 "Authenticate with Voicemail passwords";
513 static char *descrip_vmauthenticate =
514 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
515 "same way as the Authenticate application, but the passwords are taken from\n"
516 "voicemail.conf.\n"
517 " If the mailbox is specified, only that mailbox's password will be considered\n"
518 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
519 "be set with the authenticated mailbox.\n\n"
520 " Options:\n"
521 " s - Skip playing the initial prompts.\n";
523 /* Leave a message */
524 static char *app = "VoiceMail";
526 /* Check mail, control, etc */
527 static char *app2 = "VoiceMailMain";
529 static char *app3 = "MailboxExists";
530 static char *app4 = "VMAuthenticate";
532 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
533 static AST_LIST_HEAD_STATIC(zones, vm_zone);
534 static int maxsilence;
535 static int maxmsg;
536 static int silencethreshold = 128;
537 static char serveremail[80];
538 static char mailcmd[160]; /* Configurable mail cmd */
539 static char externnotify[160];
540 static struct ast_smdi_interface *smdi_iface = NULL;
541 static char vmfmts[80];
542 static double volgain;
543 static int vmminmessage;
544 static int vmmaxmessage;
545 static int maxgreet;
546 static int skipms;
547 static int maxlogins;
549 static struct ast_flags globalflags = {0};
551 static int saydurationminfo;
553 static char dialcontext[AST_MAX_CONTEXT];
554 static char callcontext[AST_MAX_CONTEXT];
555 static char exitcontext[AST_MAX_CONTEXT];
557 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
560 static char *emailbody = NULL;
561 static char *emailsubject = NULL;
562 static char *pagerbody = NULL;
563 static char *pagersubject = NULL;
564 static char fromstring[100];
565 static char pagerfromstring[100];
566 static char emailtitle[100];
567 static char charset[32] = "ISO-8859-1";
569 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
570 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
571 static int adsiver = 1;
572 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
575 static void populate_defaults(struct ast_vm_user *vmu)
577 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
578 if (saydurationminfo)
579 vmu->saydurationm = saydurationminfo;
580 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
581 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
582 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
583 if (maxmsg)
584 vmu->maxmsg = maxmsg;
585 vmu->volgain = volgain;
588 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
590 int x;
591 if (!strcasecmp(var, "attach")) {
592 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
593 } else if (!strcasecmp(var, "attachfmt")) {
594 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
595 } else if (!strcasecmp(var, "serveremail")) {
596 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
597 } else if (!strcasecmp(var, "language")) {
598 ast_copy_string(vmu->language, value, sizeof(vmu->language));
599 } else if (!strcasecmp(var, "tz")) {
600 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
601 #ifdef IMAP_STORAGE
602 } else if (!strcasecmp(var, "imapuser")) {
603 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
604 } else if (!strcasecmp(var, "imappassword")) {
605 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
606 #endif
607 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
608 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
609 } else if (!strcasecmp(var, "saycid")){
610 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
611 } else if (!strcasecmp(var,"sendvoicemail")){
612 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
613 } else if (!strcasecmp(var, "review")){
614 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
615 } else if (!strcasecmp(var, "tempgreetwarn")){
616 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
617 } else if (!strcasecmp(var, "operator")){
618 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
619 } else if (!strcasecmp(var, "envelope")){
620 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
621 } else if (!strcasecmp(var, "sayduration")){
622 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
623 } else if (!strcasecmp(var, "saydurationm")){
624 if (sscanf(value, "%d", &x) == 1) {
625 vmu->saydurationm = x;
626 } else {
627 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
629 } else if (!strcasecmp(var, "forcename")){
630 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
631 } else if (!strcasecmp(var, "forcegreetings")){
632 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
633 } else if (!strcasecmp(var, "callback")) {
634 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
635 } else if (!strcasecmp(var, "dialout")) {
636 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
637 } else if (!strcasecmp(var, "exitcontext")) {
638 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
639 } else if (!strcasecmp(var, "maxmsg")) {
640 vmu->maxmsg = atoi(value);
641 if (vmu->maxmsg <= 0) {
642 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
643 vmu->maxmsg = MAXMSG;
644 } else if (vmu->maxmsg > MAXMSGLIMIT) {
645 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
646 vmu->maxmsg = MAXMSGLIMIT;
648 } else if (!strcasecmp(var, "volgain")) {
649 sscanf(value, "%lf", &vmu->volgain);
650 } else if (!strcasecmp(var, "options")) {
651 apply_options(vmu, value);
655 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
657 int res;
658 if (!ast_strlen_zero(vmu->uniqueid)) {
659 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
660 if (res > 0) {
661 ast_copy_string(vmu->password, password, sizeof(vmu->password));
662 res = 0;
663 } else if (!res) {
664 res = -1;
666 return res;
668 return -1;
671 static void apply_options(struct ast_vm_user *vmu, const char *options)
672 { /* Destructively Parse options and apply */
673 char *stringp;
674 char *s;
675 char *var, *value;
676 stringp = ast_strdupa(options);
677 while ((s = strsep(&stringp, "|"))) {
678 value = s;
679 if ((var = strsep(&value, "=")) && value) {
680 apply_option(vmu, var, value);
685 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
687 struct ast_variable *tmp;
688 tmp = var;
689 while (tmp) {
690 if (!strcasecmp(tmp->name, "vmsecret")) {
691 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
692 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
693 if (ast_strlen_zero(retval->password))
694 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
695 } else if (!strcasecmp(tmp->name, "uniqueid")) {
696 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
697 } else if (!strcasecmp(tmp->name, "pager")) {
698 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
699 } else if (!strcasecmp(tmp->name, "email")) {
700 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
701 } else if (!strcasecmp(tmp->name, "fullname")) {
702 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
703 } else if (!strcasecmp(tmp->name, "context")) {
704 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
705 #ifdef IMAP_STORAGE
706 } else if (!strcasecmp(tmp->name, "imapuser")) {
707 ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
708 } else if (!strcasecmp(tmp->name, "imappassword")) {
709 ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
710 #endif
711 } else
712 apply_option(retval, tmp->name, tmp->value);
713 tmp = tmp->next;
717 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
719 struct ast_variable *var;
720 struct ast_vm_user *retval;
722 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
723 if (!ivm)
724 ast_set_flag(retval, VM_ALLOCED);
725 else
726 memset(retval, 0, sizeof(*retval));
727 if (mailbox)
728 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
729 populate_defaults(retval);
730 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
731 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
732 else
733 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
734 if (var) {
735 apply_options_full(retval, var);
736 ast_variables_destroy(var);
737 } else {
738 if (!ivm)
739 free(retval);
740 retval = NULL;
743 return retval;
746 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
748 /* This function could be made to generate one from a database, too */
749 struct ast_vm_user *vmu=NULL, *cur;
750 AST_LIST_LOCK(&users);
752 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
753 context = "default";
755 AST_LIST_TRAVERSE(&users, cur, list) {
756 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
757 break;
758 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
759 break;
761 if (cur) {
762 /* Make a copy, so that on a reload, we have no race */
763 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
764 memcpy(vmu, cur, sizeof(*vmu));
765 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
766 AST_LIST_NEXT(vmu, list) = NULL;
768 } else
769 vmu = find_user_realtime(ivm, context, mailbox);
770 AST_LIST_UNLOCK(&users);
771 return vmu;
774 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
776 /* This function could be made to generate one from a database, too */
777 struct ast_vm_user *cur;
778 int res = -1;
779 AST_LIST_LOCK(&users);
780 AST_LIST_TRAVERSE(&users, cur, list) {
781 if ((!context || !strcasecmp(context, cur->context)) &&
782 (!strcasecmp(mailbox, cur->mailbox)))
783 break;
785 if (cur) {
786 ast_copy_string(cur->password, newpass, sizeof(cur->password));
787 res = 0;
789 AST_LIST_UNLOCK(&users);
790 return res;
793 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
795 struct ast_config *cfg=NULL;
796 struct ast_variable *var=NULL;
797 struct ast_category *cat=NULL;
798 char *category=NULL, *value=NULL, *new=NULL;
799 const char *tmp=NULL;
801 if (!change_password_realtime(vmu, newpassword))
802 return;
804 /* check voicemail.conf */
805 if ((cfg = ast_config_load_with_comments(VOICEMAIL_CONFIG))) {
806 while ((category = ast_category_browse(cfg, category))) {
807 if (!strcasecmp(category, vmu->context)) {
808 tmp = ast_variable_retrieve(cfg, category, vmu->mailbox);
809 if (!tmp) {
810 ast_log(LOG_WARNING, "We could not find the mailbox.\n");
811 break;
813 value = strstr(tmp,",");
814 if (!value) {
815 ast_log(LOG_WARNING, "variable has bad format.\n");
816 break;
818 new = alloca((strlen(value)+strlen(newpassword)+1));
819 sprintf(new,"%s%s", newpassword, value);
820 if (!(cat = ast_category_get(cfg, category))) {
821 ast_log(LOG_WARNING, "Failed to get category structure.\n");
822 break;
824 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
827 /* save the results */
828 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
829 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
830 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
832 category = NULL;
833 var = NULL;
834 /* check users.conf and update the password stored for the mailbox*/
835 /* if no vmsecret entry exists create one. */
836 if ((cfg = ast_config_load_with_comments("users.conf"))) {
837 if (option_debug > 3)
838 ast_log(LOG_DEBUG, "we are looking for %s\n", vmu->mailbox);
839 while ((category = ast_category_browse(cfg, category))) {
840 if (option_debug > 3)
841 ast_log(LOG_DEBUG, "users.conf: %s\n", category);
842 if (!strcasecmp(category, vmu->mailbox)) {
843 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
844 if (option_debug > 3)
845 ast_log(LOG_DEBUG, "looks like we need to make vmsecret!\n");
846 var = ast_variable_new("vmsecret", newpassword);
848 new = alloca(strlen(newpassword)+1);
849 sprintf(new, "%s", newpassword);
850 if (!(cat = ast_category_get(cfg, category))) {
851 if (option_debug > 3)
852 ast_log(LOG_DEBUG, "failed to get category!\n");
853 break;
855 if (!var)
856 ast_variable_update(cat, "vmsecret", new, NULL, 0);
857 else
858 ast_variable_append(cat, var);
861 /* save the results and clean things up */
862 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
863 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
864 config_text_file_save("users.conf", cfg, "AppVoicemail");
868 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
870 char buf[255];
871 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
872 if (!ast_safe_system(buf)) {
873 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
874 /* Reset the password in memory, too */
875 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
879 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
881 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
884 #ifdef IMAP_STORAGE
885 static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num)
887 if (mkdir(dir, 01777) && (errno != EEXIST)) {
888 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
889 return snprintf(dest, len, "%s/msg%04d", dir, num);
891 return snprintf(dest, len, "%s/msg%04d", dir, num);
894 static void vm_imap_delete(int msgnum, struct vm_state *vms)
896 unsigned long messageNum = 0;
897 char arg[10];
899 /* find real message number based on msgnum */
900 /* this may be an index into vms->msgArray based on the msgnum. */
902 messageNum = vms->msgArray[msgnum];
903 if (messageNum == 0) {
904 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
905 return;
907 if (option_debug > 2)
908 ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
909 /* delete message */
910 snprintf (arg, sizeof(arg), "%lu",messageNum);
911 mail_setflag (vms->mailstream,arg,"\\DELETED");
914 #endif
915 static int make_file(char *dest, int len, char *dir, int num)
917 return snprintf(dest, len, "%s/msg%04d", dir, num);
920 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
921 * \param dest String. base directory.
922 * \param len Length of dest.
923 * \param context String. Ignored if is null or empty string.
924 * \param ext String. Ignored if is null or empty string.
925 * \param folder String. Ignored if is null or empty string.
926 * \return -1 on failure, 0 on success.
928 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
930 mode_t mode = VOICEMAIL_DIR_MODE;
932 if (!ast_strlen_zero(context)) {
933 make_dir(dest, len, context, "", "");
934 if (mkdir(dest, mode) && errno != EEXIST) {
935 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
936 return -1;
939 if (!ast_strlen_zero(ext)) {
940 make_dir(dest, len, context, ext, "");
941 if (mkdir(dest, mode) && errno != EEXIST) {
942 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
943 return -1;
946 if (!ast_strlen_zero(folder)) {
947 make_dir(dest, len, context, ext, folder);
948 if (mkdir(dest, mode) && errno != EEXIST) {
949 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
950 return -1;
953 return 0;
956 /* only return failure if ast_lock_path returns 'timeout',
957 not if the path does not exist or any other reason
959 static int vm_lock_path(const char *path)
961 switch (ast_lock_path(path)) {
962 case AST_LOCK_TIMEOUT:
963 return -1;
964 default:
965 return 0;
970 #ifdef ODBC_STORAGE
971 struct generic_prepare_struct {
972 char *sql;
973 int argc;
974 char **argv;
977 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
979 struct generic_prepare_struct *gps = data;
980 int res, i;
981 SQLHSTMT stmt;
983 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
984 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
985 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
986 return NULL;
988 res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
989 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
990 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
991 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
992 return NULL;
994 for (i = 0; i < gps->argc; i++)
995 SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
997 return stmt;
1000 static int retrieve_file(char *dir, int msgnum)
1002 int x = 0;
1003 int res;
1004 int fd=-1;
1005 size_t fdlen = 0;
1006 void *fdm = MAP_FAILED;
1007 SQLSMALLINT colcount=0;
1008 SQLHSTMT stmt;
1009 char sql[PATH_MAX];
1010 char fmt[80]="";
1011 char *c;
1012 char coltitle[256];
1013 SQLSMALLINT collen;
1014 SQLSMALLINT datatype;
1015 SQLSMALLINT decimaldigits;
1016 SQLSMALLINT nullable;
1017 SQLULEN colsize;
1018 SQLLEN colsize2;
1019 FILE *f=NULL;
1020 char rowdata[80];
1021 char fn[PATH_MAX];
1022 char full_fn[PATH_MAX];
1023 char msgnums[80];
1024 char *argv[] = { dir, msgnums };
1025 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1027 struct odbc_obj *obj;
1028 obj = ast_odbc_request_obj(odbc_database, 0);
1029 if (obj) {
1030 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1031 c = strchr(fmt, '|');
1032 if (c)
1033 *c = '\0';
1034 if (!strcasecmp(fmt, "wav49"))
1035 strcpy(fmt, "WAV");
1036 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1037 if (msgnum > -1)
1038 make_file(fn, sizeof(fn), dir, msgnum);
1039 else
1040 ast_copy_string(fn, dir, sizeof(fn));
1041 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1043 if (!(f = fopen(full_fn, "w+"))) {
1044 ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
1045 goto yuck;
1048 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1049 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1050 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1051 if (!stmt) {
1052 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1053 ast_odbc_release_obj(obj);
1054 goto yuck;
1056 res = SQLFetch(stmt);
1057 if (res == SQL_NO_DATA) {
1058 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1059 ast_odbc_release_obj(obj);
1060 goto yuck;
1062 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1063 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1064 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1065 ast_odbc_release_obj(obj);
1066 goto yuck;
1068 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, 0770);
1069 if (fd < 0) {
1070 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1071 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1072 ast_odbc_release_obj(obj);
1073 goto yuck;
1075 res = SQLNumResultCols(stmt, &colcount);
1076 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1077 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1078 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1079 ast_odbc_release_obj(obj);
1080 goto yuck;
1082 if (f)
1083 fprintf(f, "[message]\n");
1084 for (x=0;x<colcount;x++) {
1085 rowdata[0] = '\0';
1086 collen = sizeof(coltitle);
1087 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
1088 &datatype, &colsize, &decimaldigits, &nullable);
1089 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1090 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1091 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1092 ast_odbc_release_obj(obj);
1093 goto yuck;
1095 if (!strcasecmp(coltitle, "recording")) {
1096 off_t offset;
1097 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
1098 fdlen = colsize2;
1099 if (fd > -1) {
1100 char tmp[1]="";
1101 lseek(fd, fdlen - 1, SEEK_SET);
1102 if (write(fd, tmp, 1) != 1) {
1103 close(fd);
1104 fd = -1;
1105 continue;
1107 /* Read out in small chunks */
1108 for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1109 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
1110 ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1111 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1112 ast_odbc_release_obj(obj);
1113 goto yuck;
1114 } else {
1115 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
1116 munmap(fdm, CHUNKSIZE);
1117 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1118 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1119 unlink(full_fn);
1120 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1121 ast_odbc_release_obj(obj);
1122 goto yuck;
1126 truncate(full_fn, fdlen);
1128 } else {
1129 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1130 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1131 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1132 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1133 ast_odbc_release_obj(obj);
1134 goto yuck;
1136 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1137 fprintf(f, "%s=%s\n", coltitle, rowdata);
1140 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1141 ast_odbc_release_obj(obj);
1142 } else
1143 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1144 yuck:
1145 if (f)
1146 fclose(f);
1147 if (fd > -1)
1148 close(fd);
1149 return x - 1;
1152 static int remove_file(char *dir, int msgnum)
1154 char fn[PATH_MAX];
1155 char full_fn[PATH_MAX];
1156 char msgnums[80];
1158 if (msgnum > -1) {
1159 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1160 make_file(fn, sizeof(fn), dir, msgnum);
1161 } else
1162 ast_copy_string(fn, dir, sizeof(fn));
1163 ast_filedelete(fn, NULL);
1164 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1165 unlink(full_fn);
1166 return 0;
1169 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1171 int x = 0;
1172 int res;
1173 SQLHSTMT stmt;
1174 char sql[PATH_MAX];
1175 char rowdata[20];
1176 char *argv[] = { dir };
1177 struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
1179 struct odbc_obj *obj;
1180 obj = ast_odbc_request_obj(odbc_database, 0);
1181 if (obj) {
1182 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1183 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1184 if (!stmt) {
1185 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1186 ast_odbc_release_obj(obj);
1187 goto yuck;
1189 res = SQLFetch(stmt);
1190 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1191 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1192 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1193 ast_odbc_release_obj(obj);
1194 goto yuck;
1196 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1197 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1198 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1199 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1200 ast_odbc_release_obj(obj);
1201 goto yuck;
1203 if (sscanf(rowdata, "%d", &x) != 1)
1204 ast_log(LOG_WARNING, "Failed to read message count!\n");
1205 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1206 ast_odbc_release_obj(obj);
1207 } else
1208 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1209 yuck:
1210 return x - 1;
1213 static int message_exists(char *dir, int msgnum)
1215 int x = 0;
1216 int res;
1217 SQLHSTMT stmt;
1218 char sql[PATH_MAX];
1219 char rowdata[20];
1220 char msgnums[20];
1221 char *argv[] = { dir, msgnums };
1222 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1224 struct odbc_obj *obj;
1225 obj = ast_odbc_request_obj(odbc_database, 0);
1226 if (obj) {
1227 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1228 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1229 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1230 if (!stmt) {
1231 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1232 ast_odbc_release_obj(obj);
1233 goto yuck;
1235 res = SQLFetch(stmt);
1236 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1237 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1238 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1239 ast_odbc_release_obj(obj);
1240 goto yuck;
1242 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1243 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1244 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1245 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1246 ast_odbc_release_obj(obj);
1247 goto yuck;
1249 if (sscanf(rowdata, "%d", &x) != 1)
1250 ast_log(LOG_WARNING, "Failed to read message count!\n");
1251 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1252 ast_odbc_release_obj(obj);
1253 } else
1254 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1255 yuck:
1256 return x;
1259 static int count_messages(struct ast_vm_user *vmu, char *dir)
1261 return last_message_index(vmu, dir) + 1;
1264 static void delete_file(char *sdir, int smsg)
1266 SQLHSTMT stmt;
1267 char sql[PATH_MAX];
1268 char msgnums[20];
1269 char *argv[] = { sdir, msgnums };
1270 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1272 struct odbc_obj *obj;
1273 obj = ast_odbc_request_obj(odbc_database, 0);
1274 if (obj) {
1275 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1276 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1277 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1278 if (!stmt)
1279 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1280 else
1281 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1282 ast_odbc_release_obj(obj);
1283 } else
1284 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1285 return;
1288 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1290 SQLHSTMT stmt;
1291 char sql[512];
1292 char msgnums[20];
1293 char msgnumd[20];
1294 struct odbc_obj *obj;
1295 char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
1296 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1298 delete_file(ddir, dmsg);
1299 obj = ast_odbc_request_obj(odbc_database, 0);
1300 if (obj) {
1301 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1302 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1303 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, mailboxuser, mailboxcontext) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,?,? FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table);
1304 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1305 if (!stmt)
1306 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1307 else
1308 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1309 ast_odbc_release_obj(obj);
1310 } else
1311 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1312 return;
1315 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1317 int x = 0;
1318 int res;
1319 int fd = -1;
1320 void *fdm = MAP_FAILED;
1321 size_t fdlen = -1;
1322 SQLHSTMT stmt;
1323 SQLLEN len;
1324 char sql[PATH_MAX];
1325 char msgnums[20];
1326 char fn[PATH_MAX];
1327 char full_fn[PATH_MAX];
1328 char fmt[80]="";
1329 char *c;
1330 const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1331 const char *category = "";
1332 struct ast_config *cfg=NULL;
1333 struct odbc_obj *obj;
1335 delete_file(dir, msgnum);
1336 obj = ast_odbc_request_obj(odbc_database, 0);
1337 if (obj) {
1338 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1339 c = strchr(fmt, '|');
1340 if (c)
1341 *c = '\0';
1342 if (!strcasecmp(fmt, "wav49"))
1343 strcpy(fmt, "WAV");
1344 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1345 if (msgnum > -1)
1346 make_file(fn, sizeof(fn), dir, msgnum);
1347 else
1348 ast_copy_string(fn, dir, sizeof(fn));
1349 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1350 cfg = ast_config_load(full_fn);
1351 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1352 fd = open(full_fn, O_RDWR);
1353 if (fd < 0) {
1354 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1355 ast_odbc_release_obj(obj);
1356 goto yuck;
1358 if (cfg) {
1359 context = ast_variable_retrieve(cfg, "message", "context");
1360 if (!context) context = "";
1361 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1362 if (!macrocontext) macrocontext = "";
1363 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1364 if (!callerid) callerid = "";
1365 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1366 if (!origtime) origtime = "";
1367 duration = ast_variable_retrieve(cfg, "message", "duration");
1368 if (!duration) duration = "";
1369 category = ast_variable_retrieve(cfg, "message", "category");
1370 if (!category) category = "";
1372 fdlen = lseek(fd, 0, SEEK_END);
1373 lseek(fd, 0, SEEK_SET);
1374 printf("Length is %zd\n", fdlen);
1375 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1376 if (fdm == MAP_FAILED) {
1377 ast_log(LOG_WARNING, "Memory map failed!\n");
1378 ast_odbc_release_obj(obj);
1379 goto yuck;
1381 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1382 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1383 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1384 ast_odbc_release_obj(obj);
1385 goto yuck;
1387 if (!ast_strlen_zero(category))
1388 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1389 else
1390 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1391 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1392 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1393 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1394 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1395 ast_odbc_release_obj(obj);
1396 goto yuck;
1398 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1399 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1400 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1401 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1402 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1403 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1404 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1405 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1406 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1407 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1408 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1409 if (!ast_strlen_zero(category))
1410 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1411 res = ast_odbc_smart_execute(obj, stmt);
1412 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1413 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1414 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1415 ast_odbc_release_obj(obj);
1416 goto yuck;
1418 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1419 ast_odbc_release_obj(obj);
1420 } else
1421 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1422 yuck:
1423 if (cfg)
1424 ast_config_destroy(cfg);
1425 if (fdm != MAP_FAILED)
1426 munmap(fdm, fdlen);
1427 if (fd > -1)
1428 close(fd);
1429 return x;
1432 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1434 SQLHSTMT stmt;
1435 char sql[PATH_MAX];
1436 char msgnums[20];
1437 char msgnumd[20];
1438 struct odbc_obj *obj;
1439 char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
1440 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1442 delete_file(ddir, dmsg);
1443 obj = ast_odbc_request_obj(odbc_database, 0);
1444 if (obj) {
1445 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1446 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1447 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1448 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1449 if (!stmt)
1450 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1451 else
1452 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1453 ast_odbc_release_obj(obj);
1454 } else
1455 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1456 return;
1459 #else
1460 #ifndef IMAP_STORAGE
1461 static int count_messages(struct ast_vm_user *vmu, char *dir)
1463 /* Find all .txt files - even if they are not in sequence from 0000 */
1465 int vmcount = 0;
1466 DIR *vmdir = NULL;
1467 struct dirent *vment = NULL;
1469 if (vm_lock_path(dir))
1470 return ERROR_LOCK_PATH;
1472 if ((vmdir = opendir(dir))) {
1473 while ((vment = readdir(vmdir))) {
1474 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1475 vmcount++;
1477 closedir(vmdir);
1479 ast_unlock_path(dir);
1481 return vmcount;
1484 static void rename_file(char *sfn, char *dfn)
1486 char stxt[PATH_MAX];
1487 char dtxt[PATH_MAX];
1488 ast_filerename(sfn,dfn,NULL);
1489 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1490 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1491 rename(stxt, dtxt);
1493 #endif
1496 * A negative return value indicates an error.
1498 #if (!defined(IMAP_STORAGE) && !defined(ODBC_STORAGE))
1499 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1501 int x;
1502 char fn[PATH_MAX];
1504 if (vm_lock_path(dir))
1505 return ERROR_LOCK_PATH;
1507 for (x = 0; x < vmu->maxmsg; x++) {
1508 make_file(fn, sizeof(fn), dir, x);
1509 if (ast_fileexists(fn, NULL, NULL) < 1)
1510 break;
1512 ast_unlock_path(dir);
1514 return x - 1;
1516 #endif
1517 #endif
1519 static int copy(char *infile, char *outfile)
1521 int ifd;
1522 int ofd;
1523 int res;
1524 int len;
1525 char buf[4096];
1527 #ifdef HARDLINK_WHEN_POSSIBLE
1528 /* Hard link if possible; saves disk space & is faster */
1529 if (link(infile, outfile)) {
1530 #endif
1531 if ((ifd = open(infile, O_RDONLY)) < 0) {
1532 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1533 return -1;
1535 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1536 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1537 close(ifd);
1538 return -1;
1540 do {
1541 len = read(ifd, buf, sizeof(buf));
1542 if (len < 0) {
1543 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1544 close(ifd);
1545 close(ofd);
1546 unlink(outfile);
1548 if (len) {
1549 res = write(ofd, buf, len);
1550 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1551 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1552 close(ifd);
1553 close(ofd);
1554 unlink(outfile);
1557 } while (len);
1558 close(ifd);
1559 close(ofd);
1560 return 0;
1561 #ifdef HARDLINK_WHEN_POSSIBLE
1562 } else {
1563 /* Hard link succeeded */
1564 return 0;
1566 #endif
1569 static void copy_plain_file(char *frompath, char *topath)
1571 char frompath2[PATH_MAX], topath2[PATH_MAX];
1572 ast_filecopy(frompath, topath, NULL);
1573 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1574 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1575 copy(frompath2, topath2);
1578 static int vm_delete(char *file)
1580 char *txt;
1581 int txtsize = 0;
1583 txtsize = (strlen(file) + 5)*sizeof(char);
1584 txt = alloca(txtsize);
1585 /* Sprintf here would safe because we alloca'd exactly the right length,
1586 * but trying to eliminate all sprintf's anyhow
1588 snprintf(txt, txtsize, "%s.txt", file);
1589 unlink(txt);
1590 return ast_filedelete(file, NULL);
1593 static int inbuf(struct baseio *bio, FILE *fi)
1595 int l;
1597 if (bio->ateof)
1598 return 0;
1600 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1601 if (ferror(fi))
1602 return -1;
1604 bio->ateof = 1;
1605 return 0;
1608 bio->iolen= l;
1609 bio->iocp= 0;
1611 return 1;
1614 static int inchar(struct baseio *bio, FILE *fi)
1616 if (bio->iocp>=bio->iolen) {
1617 if (!inbuf(bio, fi))
1618 return EOF;
1621 return bio->iobuf[bio->iocp++];
1624 static int ochar(struct baseio *bio, int c, FILE *so)
1626 if (bio->linelength>=BASELINELEN) {
1627 if (fputs(eol,so)==EOF)
1628 return -1;
1630 bio->linelength= 0;
1633 if (putc(((unsigned char)c),so)==EOF)
1634 return -1;
1636 bio->linelength++;
1638 return 1;
1641 static int base_encode(char *filename, FILE *so)
1643 unsigned char dtable[BASEMAXINLINE];
1644 int i,hiteof= 0;
1645 FILE *fi;
1646 struct baseio bio;
1648 memset(&bio, 0, sizeof(bio));
1649 bio.iocp = BASEMAXINLINE;
1651 if (!(fi = fopen(filename, "rb"))) {
1652 ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
1653 return -1;
1656 for (i= 0;i<9;i++) {
1657 dtable[i]= 'A'+i;
1658 dtable[i+9]= 'J'+i;
1659 dtable[26+i]= 'a'+i;
1660 dtable[26+i+9]= 'j'+i;
1662 for (i= 0;i<8;i++) {
1663 dtable[i+18]= 'S'+i;
1664 dtable[26+i+18]= 's'+i;
1666 for (i= 0;i<10;i++) {
1667 dtable[52+i]= '0'+i;
1669 dtable[62]= '+';
1670 dtable[63]= '/';
1672 while (!hiteof){
1673 unsigned char igroup[3],ogroup[4];
1674 int c,n;
1676 igroup[0]= igroup[1]= igroup[2]= 0;
1678 for (n= 0;n<3;n++) {
1679 if ((c = inchar(&bio, fi)) == EOF) {
1680 hiteof= 1;
1681 break;
1684 igroup[n]= (unsigned char)c;
1687 if (n> 0) {
1688 ogroup[0]= dtable[igroup[0]>>2];
1689 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1690 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1691 ogroup[3]= dtable[igroup[2]&0x3F];
1693 if (n<3) {
1694 ogroup[3]= '=';
1696 if (n<2)
1697 ogroup[2]= '=';
1700 for (i= 0;i<4;i++)
1701 ochar(&bio, ogroup[i], so);
1705 fclose(fi);
1707 if (fputs(eol,so)==EOF)
1708 return 0;
1710 return 1;
1713 static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize, const char *category)
1715 char callerid[256];
1716 /* Prepare variables for substition in email body and subject */
1717 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1718 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1719 snprintf(passdata, passdatasize, "%d", msgnum);
1720 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1721 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1722 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1723 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1724 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1725 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1726 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1727 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1730 static char *quote(const char *from, char *to, size_t len)
1732 char *ptr = to;
1733 *ptr++ = '"';
1734 for (; ptr < to + len - 1; from++) {
1735 if (*from == '"')
1736 *ptr++ = '\\';
1737 else if (*from == '\0')
1738 break;
1739 *ptr++ = *from;
1741 if (ptr < to + len - 1)
1742 *ptr++ = '"';
1743 *ptr = '\0';
1744 return to;
1747 * fill in *tm for current time according to the proper timezone, if any.
1748 * Return tm so it can be used as a function argument.
1750 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1752 const struct vm_zone *z = NULL;
1753 time_t t = time(NULL);
1755 /* Does this user have a timezone specified? */
1756 if (!ast_strlen_zero(vmu->zonetag)) {
1757 /* Find the zone in the list */
1758 AST_LIST_LOCK(&zones);
1759 AST_LIST_TRAVERSE(&zones, z, list) {
1760 if (!strcmp(z->name, vmu->zonetag))
1761 break;
1763 AST_LIST_UNLOCK(&zones);
1765 ast_localtime(&t, tm, z ? z->timezone : NULL);
1766 return tm;
1769 /* same as mkstemp, but return a FILE * */
1770 static FILE *vm_mkftemp(char *template)
1772 FILE *p = NULL;
1773 int pfd = mkstemp(template);
1774 chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
1775 if (pfd > -1) {
1776 p = fdopen(pfd, "w+");
1777 if (!p) {
1778 close(pfd);
1779 pfd = -1;
1782 return p;
1785 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap)
1787 char date[256];
1788 char host[MAXHOSTNAMELEN] = "";
1789 char who[256];
1790 char bound[256];
1791 char fname[256];
1792 char dur[256];
1793 char tmpcmd[256];
1794 struct tm tm;
1795 char *passdata2;
1796 size_t len_passdata;
1797 #ifdef IMAP_STORAGE
1798 #define ENDL "\r\n"
1799 #else
1800 #define ENDL "\n"
1801 #endif
1803 gethostname(host, sizeof(host) - 1);
1804 if (strchr(srcemail, '@'))
1805 ast_copy_string(who, srcemail, sizeof(who));
1806 else {
1807 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1809 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1810 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1811 fprintf(p, "Date: %s" ENDL, date);
1813 /* Set date format for voicemail mail */
1814 strftime(date, sizeof(date), emaildateformat, &tm);
1816 if (*fromstring) {
1817 struct ast_channel *ast;
1818 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1819 char *passdata;
1820 int vmlen = strlen(fromstring)*3 + 200;
1821 if ((passdata = alloca(vmlen))) {
1822 memset(passdata, 0, vmlen);
1823 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1824 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1825 len_passdata = strlen(passdata) * 2 + 3;
1826 passdata2 = alloca(len_passdata);
1827 fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
1828 } else
1829 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1830 ast_channel_free(ast);
1831 } else
1832 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1833 } else
1834 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
1835 len_passdata = strlen(vmu->fullname) * 2 + 3;
1836 passdata2 = alloca(len_passdata);
1837 fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
1838 if (emailsubject) {
1839 struct ast_channel *ast;
1840 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1841 char *passdata;
1842 int vmlen = strlen(emailsubject)*3 + 200;
1843 if ((passdata = alloca(vmlen))) {
1844 memset(passdata, 0, vmlen);
1845 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1846 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
1847 fprintf(p, "Subject: %s" ENDL, passdata);
1848 } else
1849 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1850 ast_channel_free(ast);
1851 } else
1852 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1853 } else if (*emailtitle) {
1854 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1855 fprintf(p, ENDL) ;
1856 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1857 fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
1858 else
1859 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
1860 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
1861 if (imap) {
1862 /* additional information needed for IMAP searching */
1863 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
1864 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
1865 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
1866 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
1867 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
1868 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
1869 fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
1870 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
1871 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
1872 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
1873 if (!ast_strlen_zero(category))
1874 fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
1875 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
1876 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
1878 if (!ast_strlen_zero(cidnum))
1879 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, cidnum);
1880 if (!ast_strlen_zero(cidname))
1881 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, cidname);
1882 fprintf(p, "MIME-Version: 1.0" ENDL);
1883 if (attach_user_voicemail) {
1884 /* Something unique. */
1885 snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
1887 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
1888 fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
1889 fprintf(p, "--%s" ENDL, bound);
1891 fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
1892 if (emailbody) {
1893 struct ast_channel *ast;
1894 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1895 char *passdata;
1896 int vmlen = strlen(emailbody)*3 + 200;
1897 if ((passdata = alloca(vmlen))) {
1898 memset(passdata, 0, vmlen);
1899 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1900 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
1901 fprintf(p, "%s" ENDL, passdata);
1902 } else
1903 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1904 ast_channel_free(ast);
1905 } else
1906 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1907 } else {
1908 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
1910 "in mailbox %s from %s, on %s so you might" ENDL
1911 "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname,
1912 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1914 if (attach_user_voicemail) {
1915 /* Eww. We want formats to tell us their own MIME type */
1916 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
1917 char tmpdir[256], newtmp[256];
1918 int tmpfd = -1;
1920 if (vmu->volgain < -.001 || vmu->volgain > .001) {
1921 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
1922 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
1923 tmpfd = mkstemp(newtmp);
1924 chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
1925 if (option_debug > 2)
1926 ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
1927 if (tmpfd > -1) {
1928 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
1929 ast_safe_system(tmpcmd);
1930 attach = newtmp;
1931 if (option_debug > 2)
1932 ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
1935 fprintf(p, "--%s" ENDL, bound);
1936 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
1937 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
1938 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
1939 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
1940 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1941 base_encode(fname, p);
1942 fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
1943 if (tmpfd > -1) {
1944 unlink(fname);
1945 close(tmpfd);
1946 unlink(newtmp);
1949 #undef ENDL
1951 static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category)
1953 FILE *p=NULL;
1954 char tmp[80] = "/tmp/astmail-XXXXXX";
1955 char tmp2[256];
1957 if (vmu && ast_strlen_zero(vmu->email)) {
1958 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
1959 return(0);
1961 if (!strcmp(format, "wav49"))
1962 format = "WAV";
1963 if (option_debug > 2)
1964 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
1965 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1966 command hangs */
1967 if ((p = vm_mkftemp(tmp)) == NULL) {
1968 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1969 return -1;
1970 } else {
1971 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
1972 fclose(p);
1973 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1974 ast_safe_system(tmp2);
1975 if (option_debug > 2)
1976 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
1978 return 0;
1981 static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category)
1983 char date[256];
1984 char host[MAXHOSTNAMELEN] = "";
1985 char who[256];
1986 char dur[PATH_MAX];
1987 char tmp[80] = "/tmp/astmail-XXXXXX";
1988 char tmp2[PATH_MAX];
1989 struct tm tm;
1990 FILE *p;
1992 if ((p = vm_mkftemp(tmp)) == NULL) {
1993 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1994 return -1;
1995 } else {
1996 gethostname(host, sizeof(host)-1);
1997 if (strchr(srcemail, '@'))
1998 ast_copy_string(who, srcemail, sizeof(who));
1999 else {
2000 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2002 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2003 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2004 fprintf(p, "Date: %s\n", date);
2006 if (*pagerfromstring) {
2007 struct ast_channel *ast;
2008 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2009 char *passdata;
2010 int vmlen = strlen(fromstring)*3 + 200;
2011 if ((passdata = alloca(vmlen))) {
2012 memset(passdata, 0, vmlen);
2013 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2014 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2015 fprintf(p, "From: %s <%s>\n", passdata, who);
2016 } else
2017 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2018 ast_channel_free(ast);
2019 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2020 } else
2021 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2022 fprintf(p, "To: %s\n", pager);
2023 if (pagersubject) {
2024 struct ast_channel *ast;
2025 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2026 char *passdata;
2027 int vmlen = strlen(pagersubject) * 3 + 200;
2028 if ((passdata = alloca(vmlen))) {
2029 memset(passdata, 0, vmlen);
2030 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2031 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2032 fprintf(p, "Subject: %s\n\n", passdata);
2033 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2034 ast_channel_free(ast);
2035 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2036 } else
2037 fprintf(p, "Subject: New VM\n\n");
2038 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2039 if (pagerbody) {
2040 struct ast_channel *ast;
2041 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2042 char *passdata;
2043 int vmlen = strlen(pagerbody)*3 + 200;
2044 if ((passdata = alloca(vmlen))) {
2045 memset(passdata, 0, vmlen);
2046 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2047 pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2048 fprintf(p, "%s\n", passdata);
2049 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2050 ast_channel_free(ast);
2051 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2052 } else {
2053 fprintf(p, "New %s long msg in box %s\n"
2054 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2056 fclose(p);
2057 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2058 ast_safe_system(tmp2);
2059 if (option_debug > 2)
2060 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
2062 return 0;
2065 static int get_date(char *s, int len)
2067 struct tm tm;
2068 time_t t;
2070 time(&t);
2072 ast_localtime(&t, &tm, NULL);
2074 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2077 static int play_greeting(struct ast_channel *chan, struct ast_vm_user *vmu, char *filename, char *ecodes)
2079 int res = -2;
2081 #ifdef ODBC_STORAGE
2082 int success =
2083 #endif
2084 RETRIEVE(filename, -1);
2085 if (ast_fileexists(filename, NULL, NULL) > 0) {
2086 res = ast_streamfile(chan, filename, chan->language);
2087 if (res > -1)
2088 res = ast_waitstream(chan, ecodes);
2089 #ifdef ODBC_STORAGE
2090 if (success == -1) {
2091 /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
2092 if (option_debug)
2093 ast_log(LOG_DEBUG, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
2094 store_file(filename, vmu->mailbox, vmu->context, -1);
2096 #endif
2098 DISPOSE(filename, -1);
2100 return res;
2103 static int invent_message(struct ast_channel *chan, struct ast_vm_user *vmu, char *ext, int busy, char *ecodes)
2105 int res;
2106 char fn[PATH_MAX];
2107 char dest[PATH_MAX];
2109 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, ext);
2111 if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "greet"))) {
2112 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
2113 return -1;
2116 res = play_greeting(chan, vmu, fn, ecodes);
2117 if (res == -2) {
2118 /* File did not exist */
2119 res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
2120 if (res)
2121 return res;
2122 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2125 if (res)
2126 return res;
2128 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
2129 return res;
2132 static void free_user(struct ast_vm_user *vmu)
2134 if (ast_test_flag(vmu, VM_ALLOCED))
2135 free(vmu);
2138 static void free_zone(struct vm_zone *z)
2140 free(z);
2143 static const char *mbox(int id)
2145 static const char *msgs[] = {
2146 "INBOX",
2147 "Old",
2148 "Work",
2149 "Family",
2150 "Friends",
2151 "Cust1",
2152 "Cust2",
2153 "Cust3",
2154 "Cust4",
2155 "Cust5",
2157 return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "tmp";
2159 #ifdef IMAP_STORAGE
2160 static int folder_int(const char *folder)
2162 /*assume a NULL folder means INBOX*/
2163 if (!folder)
2164 return 0;
2165 if (!strcasecmp(folder, "INBOX"))
2166 return 0;
2167 else if (!strcasecmp(folder, "Old"))
2168 return 1;
2169 else if (!strcasecmp(folder, "Work"))
2170 return 2;
2171 else if (!strcasecmp(folder, "Family"))
2172 return 3;
2173 else if (!strcasecmp(folder, "Friends"))
2174 return 4;
2175 else if (!strcasecmp(folder, "Cust1"))
2176 return 5;
2177 else if (!strcasecmp(folder, "Cust2"))
2178 return 6;
2179 else if (!strcasecmp(folder, "Cust3"))
2180 return 7;
2181 else if (!strcasecmp(folder, "Cust4"))
2182 return 8;
2183 else if (!strcasecmp(folder, "Cust5"))
2184 return 9;
2185 else /*assume they meant INBOX if folder is not found otherwise*/
2186 return 0;
2188 #endif
2190 #ifdef ODBC_STORAGE
2191 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
2192 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2194 int x = -1;
2195 int res;
2196 SQLHSTMT stmt;
2197 char sql[PATH_MAX];
2198 char rowdata[20];
2199 char tmp[PATH_MAX] = "";
2200 struct odbc_obj *obj;
2201 char *context;
2202 struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2204 if (newmsgs)
2205 *newmsgs = 0;
2206 if (oldmsgs)
2207 *oldmsgs = 0;
2209 /* If no mailbox, return immediately */
2210 if (ast_strlen_zero(mailbox))
2211 return 0;
2213 ast_copy_string(tmp, mailbox, sizeof(tmp));
2215 context = strchr(tmp, '@');
2216 if (context) {
2217 *context = '\0';
2218 context++;
2219 } else
2220 context = "default";
2222 obj = ast_odbc_request_obj(odbc_database, 0);
2223 if (obj) {
2224 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2225 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2226 if (!stmt) {
2227 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2228 ast_odbc_release_obj(obj);
2229 goto yuck;
2231 res = SQLFetch(stmt);
2232 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2233 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2234 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2235 ast_odbc_release_obj(obj);
2236 goto yuck;
2238 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2239 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2240 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2241 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2242 ast_odbc_release_obj(obj);
2243 goto yuck;
2245 *newmsgs = atoi(rowdata);
2246 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2248 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2249 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2250 if (!stmt) {
2251 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2252 ast_odbc_release_obj(obj);
2253 goto yuck;
2255 res = SQLFetch(stmt);
2256 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2257 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2258 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2259 ast_odbc_release_obj(obj);
2260 goto yuck;
2262 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2263 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2264 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2265 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2266 ast_odbc_release_obj(obj);
2267 goto yuck;
2269 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2270 ast_odbc_release_obj(obj);
2271 *oldmsgs = atoi(rowdata);
2272 x = 0;
2273 } else
2274 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2276 yuck:
2277 return x;
2280 static int messagecount(const char *context, const char *mailbox, const char *folder)
2282 struct odbc_obj *obj = NULL;
2283 int nummsgs = 0;
2284 int res;
2285 SQLHSTMT stmt = NULL;
2286 char sql[PATH_MAX];
2287 char rowdata[20];
2288 struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2289 if (!folder)
2290 folder = "INBOX";
2291 /* If no mailbox, return immediately */
2292 if (ast_strlen_zero(mailbox))
2293 return 0;
2295 obj = ast_odbc_request_obj(odbc_database, 0);
2296 if (obj) {
2297 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2298 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2299 if (!stmt) {
2300 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2301 goto yuck;
2303 res = SQLFetch(stmt);
2304 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2305 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2306 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2307 goto yuck;
2309 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2310 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2311 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2312 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2313 goto yuck;
2315 nummsgs = atoi(rowdata);
2316 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2317 } else
2318 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2320 yuck:
2321 if (obj)
2322 ast_odbc_release_obj(obj);
2323 return nummsgs;
2326 static int has_voicemail(const char *mailbox, const char *folder)
2328 char tmp[256], *tmp2 = tmp, *mbox, *context;
2329 ast_copy_string(tmp, mailbox, sizeof(tmp));
2330 while ((context = mbox = strsep(&tmp2, ","))) {
2331 strsep(&context, "@");
2332 if (ast_strlen_zero(context))
2333 context = "default";
2334 if (messagecount(context, mbox, folder))
2335 return 1;
2337 return 0;
2340 #elif defined(IMAP_STORAGE)
2342 static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms)
2344 char *myserveremail = serveremail;
2345 char fn[PATH_MAX];
2346 char mailbox[256];
2347 char *stringp;
2348 FILE *p=NULL;
2349 char tmp[80] = "/tmp/astmail-XXXXXX";
2350 long len;
2351 void *buf;
2352 int tempcopy = 0;
2353 STRING str;
2355 /*Greetings are not retrieved from IMAP, so there is no reason to attempt storing them there either*/
2356 if (msgnum < 0)
2357 return 0;
2359 /* Attach only the first format */
2360 fmt = ast_strdupa(fmt);
2361 stringp = fmt;
2362 strsep(&stringp, "|");
2364 if (!ast_strlen_zero(vmu->serveremail))
2365 myserveremail = vmu->serveremail;
2367 make_file(fn, sizeof(fn), dir, msgnum);
2369 if (ast_strlen_zero(vmu->email)) {
2370 /*we need the vmu->email to be set when we call make_email_file, but if we keep it set,
2371 * a duplicate e-mail will be created. So at the end of this function, we will revert back to an empty
2372 * string if tempcopy is 1
2374 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2375 tempcopy = 1;
2378 if (!strcmp(fmt, "wav49"))
2379 fmt = "WAV";
2380 if (option_debug > 2)
2381 ast_log(LOG_DEBUG, "Storing file '%s', format '%s'\n", fn, fmt);
2382 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2383 command hangs */
2384 if ((p = vm_mkftemp(tmp)) == NULL) {
2385 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2386 if (tempcopy)
2387 *(vmu->email) = '\0';
2388 return -1;
2389 } else {
2390 make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, fmt, duration, 1, chan, NULL, 1);
2391 /* read mail file to memory */
2392 len = ftell(p);
2393 rewind(p);
2394 if ((buf = ast_malloc(len+1)) == NIL) {
2395 ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2396 fclose(p);
2397 return -1;
2399 fread(buf, len, 1, p);
2400 ((char *)buf)[len] = '\0';
2401 INIT(&str, mail_string, buf, len);
2402 init_mailstream(vms, 0);
2403 imap_mailbox_name(mailbox, sizeof(mailbox), vms, 0, 1);
2404 if (!mail_append(vms->mailstream, mailbox, &str))
2405 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2406 fclose(p);
2407 unlink(tmp);
2408 ast_free(buf);
2409 if (option_debug > 2)
2410 ast_log(LOG_DEBUG, "%s stored\n", fn);
2412 if (tempcopy)
2413 *(vmu->email) = '\0';
2414 return 0;
2418 static int messagecount(const char *context, const char *mailbox, const char *folder)
2420 SEARCHPGM *pgm;
2421 SEARCHHEADER *hdr;
2423 struct ast_vm_user *vmu, vmus;
2424 struct vm_state *vms_p;
2425 int ret = 0;
2426 int fold = folder_int(folder);
2428 if (ast_strlen_zero(mailbox))
2429 return 0;
2431 /* We have to get the user before we can open the stream! */
2432 /* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2433 vmu = find_user(&vmus, context, mailbox);
2434 if (!vmu) {
2435 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailbox,context);
2436 return -1;
2437 } else {
2438 /* No IMAP account available */
2439 if (vmu->imapuser[0] == '\0') {
2440 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2441 return -1;
2445 /* check if someone is accessing this box right now... */
2446 vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
2447 if (!vms_p) {
2448 vms_p = get_vm_state_by_mailbox(mailbox,1);
2450 if (vms_p) {
2451 if (option_debug > 2)
2452 ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
2453 if (fold == 0) {/*INBOX*/
2454 return vms_p->newmessages;
2456 if (fold == 1) {/*Old messages*/
2457 return vms_p->oldmessages;
2461 /* add one if not there... */
2462 vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
2463 if (!vms_p) {
2464 vms_p = get_vm_state_by_mailbox(mailbox,0);
2467 if (!vms_p) {
2468 if (option_debug > 2)
2469 ast_log (LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2470 if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
2471 return -1;
2473 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2474 ast_copy_string(vms_p->username, mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
2475 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2476 if (option_debug > 2)
2477 ast_log (LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2478 vms_p->updated = 1;
2479 /* set mailbox to INBOX! */
2480 ast_copy_string(vms_p->curbox, mbox(fold), sizeof(vms_p->curbox));
2481 init_vm_state(vms_p);
2482 vmstate_insert(vms_p);
2484 ret = init_mailstream(vms_p, fold);
2485 if (!vms_p->mailstream) {
2486 ast_log (LOG_ERROR,"IMAP mailstream is NULL\n");
2487 return -1;
2489 if (ret == 0) {
2490 pgm = mail_newsearchpgm ();
2491 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
2492 pgm->header = hdr;
2493 if (fold != 1) {
2494 pgm->unseen = 1;
2495 pgm->seen = 0;
2497 /* In the special case where fold is 1 (old messages) we have to do things a bit
2498 * differently. Old messages are stored in the INBOX but are marked as "seen"
2500 else {
2501 pgm->unseen = 0;
2502 pgm->seen = 1;
2504 pgm->undeleted = 1;
2505 pgm->deleted = 0;
2507 vms_p->vmArrayIndex = 0;
2508 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2509 if (fold == 0)
2510 vms_p->newmessages = vms_p->vmArrayIndex;
2511 if (fold == 1)
2512 vms_p->oldmessages = vms_p->vmArrayIndex;
2513 /*Freeing the searchpgm also frees the searchhdr*/
2514 mail_free_searchpgm(&pgm);
2515 vms_p->updated = 0;
2516 return vms_p->vmArrayIndex;
2517 } else {
2518 mail_ping(vms_p->mailstream);
2520 return 0;
2522 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
2524 char tmp[PATH_MAX] = "";
2525 char *mailboxnc;
2526 char *context;
2527 char *mb;
2528 char *cur;
2529 if (newmsgs)
2530 *newmsgs = 0;
2531 if (oldmsgs)
2532 *oldmsgs = 0;
2534 if (option_debug > 2)
2535 ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox_context);
2536 /* If no mailbox, return immediately */
2537 if (ast_strlen_zero(mailbox_context))
2538 return 0;
2540 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2541 context = strchr(tmp, '@');
2542 if (strchr(mailbox_context, ',')) {
2543 int tmpnew, tmpold;
2544 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2545 mb = tmp;
2546 while ((cur = strsep(&mb, ", "))) {
2547 if (!ast_strlen_zero(cur)) {
2548 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2549 return -1;
2550 else {
2551 if (newmsgs)
2552 *newmsgs += tmpnew;
2553 if (oldmsgs)
2554 *oldmsgs += tmpold;
2558 return 0;
2560 if (context) {
2561 *context = '\0';
2562 mailboxnc = tmp;
2563 context++;
2564 } else {
2565 context = "default";
2566 mailboxnc = (char *)mailbox_context;
2568 if (newmsgs) {
2569 if ((*newmsgs = messagecount(context, mailboxnc, "INBOX")) < 0)
2570 return -1;
2572 if (oldmsgs) {
2573 if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
2574 return -1;
2576 return 0;
2580 static int has_voicemail(const char *mailbox, const char *folder)
2582 char tmp[256], *tmp2, *mbox, *context;
2583 ast_copy_string(tmp, mailbox, sizeof(tmp));
2584 tmp2 = tmp;
2585 if (strchr(tmp2, ',')) {
2586 while ((mbox = strsep(&tmp2, ","))) {
2587 if (!ast_strlen_zero(mbox)) {
2588 if (has_voicemail(mbox, folder))
2589 return 1;
2593 if ((context= strchr(tmp, '@')))
2594 *context++ = '\0';
2595 else
2596 context = "default";
2597 return messagecount(context, tmp, folder) ? 1 : 0;
2600 static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir)
2602 struct vm_state *sendvms = NULL, *destvms = NULL;
2603 char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2604 if (msgnum >= recip->maxmsg) {
2605 ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
2606 return -1;
2608 if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
2609 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
2610 return -1;
2612 if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
2613 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
2614 return -1;
2616 snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
2617 if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T))
2618 return 0;
2619 ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
2620 return -1;
2623 #endif
2624 #ifndef IMAP_STORAGE
2625 /* copy message only used by file storage */
2626 static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir)
2628 char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
2629 const char *frombox = mbox(imbox);
2630 int recipmsgnum;
2632 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2634 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2636 if (!dir)
2637 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2638 else
2639 ast_copy_string(fromdir, dir, sizeof(fromdir));
2641 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2643 if (vm_lock_path(todir))
2644 return ERROR_LOCK_PATH;
2646 recipmsgnum = 0;
2647 do {
2648 make_file(topath, sizeof(topath), todir, recipmsgnum);
2649 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
2650 break;
2651 recipmsgnum++;
2652 } while (recipmsgnum < recip->maxmsg);
2653 if (recipmsgnum < recip->maxmsg) {
2654 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2655 } else {
2656 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2658 ast_unlock_path(todir);
2659 notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2661 return 0;
2663 #endif
2664 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2665 static int messagecount(const char *context, const char *mailbox, const char *folder)
2667 return __has_voicemail(context, mailbox, folder, 0);
2671 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2673 DIR *dir;
2674 struct dirent *de;
2675 char fn[256];
2676 int ret = 0;
2677 if (!folder)
2678 folder = "INBOX";
2679 /* If no mailbox, return immediately */
2680 if (ast_strlen_zero(mailbox))
2681 return 0;
2682 if (!context)
2683 context = "default";
2684 snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2685 dir = opendir(fn);
2686 if (!dir)
2687 return 0;
2688 while ((de = readdir(dir))) {
2689 if (!strncasecmp(de->d_name, "msg", 3)) {
2690 if (shortcircuit) {
2691 ret = 1;
2692 break;
2693 } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2694 ret++;
2697 closedir(dir);
2698 return ret;
2702 static int has_voicemail(const char *mailbox, const char *folder)
2704 char tmp[256], *tmp2 = tmp, *mbox, *context;
2705 ast_copy_string(tmp, mailbox, sizeof(tmp));
2706 while ((mbox = strsep(&tmp2, ","))) {
2707 if ((context = strchr(mbox, '@')))
2708 *context++ = '\0';
2709 else
2710 context = "default";
2711 if (__has_voicemail(context, mbox, folder, 1))
2712 return 1;
2714 return 0;
2718 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2720 char tmp[256];
2721 char *context;
2723 if (newmsgs)
2724 *newmsgs = 0;
2725 if (oldmsgs)
2726 *oldmsgs = 0;
2727 /* If no mailbox, return immediately */
2728 if (ast_strlen_zero(mailbox))
2729 return 0;
2730 if (strchr(mailbox, ',')) {
2731 int tmpnew, tmpold;
2732 char *mb, *cur;
2734 ast_copy_string(tmp, mailbox, sizeof(tmp));
2735 mb = tmp;
2736 while ((cur = strsep(&mb, ", "))) {
2737 if (!ast_strlen_zero(cur)) {
2738 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2739 return -1;
2740 else {
2741 if (newmsgs)
2742 *newmsgs += tmpnew;
2743 if (oldmsgs)
2744 *oldmsgs += tmpold;
2748 return 0;
2750 ast_copy_string(tmp, mailbox, sizeof(tmp));
2751 context = strchr(tmp, '@');
2752 if (context) {
2753 *context = '\0';
2754 context++;
2755 } else
2756 context = "default";
2757 if (newmsgs)
2758 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2759 if (oldmsgs)
2760 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2761 return 0;
2764 #endif
2766 static void run_externnotify(char *context, char *extension)
2768 char arguments[255];
2769 char ext_context[256] = "";
2770 int newvoicemails = 0, oldvoicemails = 0;
2771 struct ast_smdi_mwi_message *mwi_msg;
2773 if (!ast_strlen_zero(context))
2774 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2775 else
2776 ast_copy_string(ext_context, extension, sizeof(ext_context));
2778 if (!strcasecmp(externnotify, "smdi")) {
2779 if (ast_app_has_voicemail(ext_context, NULL))
2780 ast_smdi_mwi_set(smdi_iface, extension);
2781 else
2782 ast_smdi_mwi_unset(smdi_iface, extension);
2784 if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
2785 ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
2786 if (!strncmp(mwi_msg->cause, "INV", 3))
2787 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2788 else if (!strncmp(mwi_msg->cause, "BLK", 3))
2789 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2790 ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2791 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2792 } else {
2793 if (option_debug)
2794 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s\n", extension);
2796 } else if (!ast_strlen_zero(externnotify)) {
2797 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2798 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2799 } else {
2800 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2801 if (option_debug)
2802 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2803 ast_safe_system(arguments);
2808 struct leave_vm_options {
2809 unsigned int flags;
2810 signed char record_gain;
2813 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2815 #ifdef IMAP_STORAGE
2816 int newmsgs, oldmsgs;
2817 struct vm_state *vms = NULL;
2818 #endif
2819 char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
2820 char callerid[256];
2821 FILE *txt;
2822 char date[256];
2823 int txtdes;
2824 int res = 0;
2825 int msgnum;
2826 int duration = 0;
2827 int ausemacro = 0;
2828 int ousemacro = 0;
2829 int ouseexten = 0;
2830 char dir[PATH_MAX], tmpdir[PATH_MAX];
2831 char dest[PATH_MAX];
2832 char fn[PATH_MAX];
2833 char prefile[PATH_MAX] = "";
2834 char tempfile[PATH_MAX] = "";
2835 char ext_context[256] = "";
2836 char fmt[80];
2837 char *context;
2838 char ecodes[16] = "#";
2839 char tmp[1024] = "", *tmpptr;
2840 struct ast_vm_user *vmu;
2841 struct ast_vm_user svm;
2842 const char *category = NULL;
2844 ast_copy_string(tmp, ext, sizeof(tmp));
2845 ext = tmp;
2846 context = strchr(tmp, '@');
2847 if (context) {
2848 *context++ = '\0';
2849 tmpptr = strchr(context, '&');
2850 } else {
2851 tmpptr = strchr(ext, '&');
2854 if (tmpptr)
2855 *tmpptr++ = '\0';
2857 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2859 if (option_debug > 2)
2860 ast_log(LOG_DEBUG, "Before find_user\n");
2861 if (!(vmu = find_user(&svm, context, ext))) {
2862 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2863 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2864 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2865 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2866 return res;
2868 /* Setup pre-file if appropriate */
2869 if (strcmp(vmu->context, "default"))
2870 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2871 else
2872 ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
2873 if (ast_test_flag(options, OPT_BUSY_GREETING)) {
2874 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
2875 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2876 } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
2877 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
2878 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2880 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2881 if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
2882 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
2883 return -1;
2885 RETRIEVE(tempfile, -1);
2886 if (ast_fileexists(tempfile, NULL, NULL) > 0)
2887 ast_copy_string(prefile, tempfile, sizeof(prefile));
2888 DISPOSE(tempfile, -1);
2889 /* It's easier just to try to make it than to check for its existence */
2890 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2891 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
2893 /* Check current or macro-calling context for special extensions */
2894 if (ast_test_flag(vmu, VM_OPERATOR)) {
2895 if (!ast_strlen_zero(vmu->exit)) {
2896 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
2897 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2898 ouseexten = 1;
2900 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
2901 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2902 ouseexten = 1;
2904 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2905 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2906 ousemacro = 1;
2910 if (!ast_strlen_zero(vmu->exit)) {
2911 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2912 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2913 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2914 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2915 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2916 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2917 ausemacro = 1;
2920 /* Play the beginning intro if desired */
2921 if (!ast_strlen_zero(prefile)) {
2922 res = play_greeting(chan, vmu, prefile, ecodes);
2923 if (res == -2) {
2924 /* The file did not exist */
2925 if (option_debug)
2926 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2927 res = invent_message(chan, vmu, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2929 if (res < 0) {
2930 if (option_debug)
2931 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2932 free_user(vmu);
2933 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2934 return -1;
2937 if (res == '#') {
2938 /* On a '#' we skip the instructions */
2939 ast_set_flag(options, OPT_SILENT);
2940 res = 0;
2942 if (!res && !ast_test_flag(options, OPT_SILENT)) {
2943 res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
2944 if (res == '#') {
2945 ast_set_flag(options, OPT_SILENT);
2946 res = 0;
2949 if (res > 0)
2950 ast_stopstream(chan);
2951 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2952 other than the operator -- an automated attendant or mailbox login for example */
2953 if (res == '*') {
2954 chan->exten[0] = 'a';
2955 chan->exten[1] = '\0';
2956 if (!ast_strlen_zero(vmu->exit)) {
2957 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2958 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2959 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2961 chan->priority = 0;
2962 free_user(vmu);
2963 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2964 return 0;
2967 /* Check for a '0' here */
2968 if (res == '0') {
2969 transfer:
2970 if (ouseexten || ousemacro) {
2971 chan->exten[0] = 'o';
2972 chan->exten[1] = '\0';
2973 if (!ast_strlen_zero(vmu->exit)) {
2974 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2975 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2976 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2978 ast_play_and_wait(chan, "transfer");
2979 chan->priority = 0;
2980 free_user(vmu);
2981 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2983 return 0;
2985 if (res < 0) {
2986 free_user(vmu);
2987 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2988 return -1;
2990 /* The meat of recording the message... All the announcements and beeps have been played*/
2991 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2992 if (!ast_strlen_zero(fmt)) {
2993 msgnum = 0;
2995 #ifdef IMAP_STORAGE
2996 /* Is ext a mailbox? */
2997 /* must open stream for this user to get info! */
2998 res = inboxcount(ext_context, &newmsgs, &oldmsgs);
2999 if (res < 0) {
3000 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
3001 return -1;
3003 if (!(vms = get_vm_state_by_mailbox(ext,0))) {
3004 /*It is possible under certain circumstances that inboxcount did not create a vm_state when it was needed. This is a catchall which will
3005 * rarely be used*/
3006 if (!(vms = ast_calloc(1, sizeof(*vms)))) {
3007 ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
3008 return -1;
3010 ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
3011 ast_copy_string(vms->username, ext, sizeof(vms->username));
3012 vms->mailstream = NIL;
3013 if (option_debug > 2)
3014 ast_log(LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms->imapuser);
3015 vms->updated=1;
3016 ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
3017 init_vm_state(vms);
3018 vmstate_insert(vms);
3019 vms = get_vm_state_by_mailbox(ext,0);
3021 vms->newmessages++;
3022 /* here is a big difference! We add one to it later */
3023 msgnum = newmsgs + oldmsgs;
3024 if (option_debug > 2)
3025 ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3026 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
3027 /* set variable for compatability */
3028 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3030 /* Check if mailbox is full */
3031 check_quota(vms, imapfolder);
3032 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
3033 if (option_debug)
3034 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
3035 ast_play_and_wait(chan, "vm-mailboxfull");
3036 return -1;
3038 if (option_debug > 2)
3039 ast_log(LOG_DEBUG, "Checking message number quota - mailbox has %d messages, maximum is set to %d\n",msgnum,vmu->maxmsg);
3040 if (msgnum >= vmu->maxmsg) {
3041 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3042 if (!res)
3043 res = ast_waitstream(chan, "");
3044 ast_log(LOG_WARNING, "No more messages possible\n");
3045 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3046 goto leave_vm_out;
3049 /* Check if we have exceeded maxmsg */
3050 if (msgnum >= vmu->maxmsg) {
3051 ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u > %u)\n", msgnum, vmu->maxmsg);
3052 ast_play_and_wait(chan, "vm-mailboxfull");
3053 return -1;
3055 /* here is a big difference! We add one to it later */
3056 if (option_debug > 2)
3057 ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3058 #else
3059 if (count_messages(vmu, dir) >= vmu->maxmsg) {
3060 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3061 if (!res)
3062 res = ast_waitstream(chan, "");
3063 ast_log(LOG_WARNING, "No more messages possible\n");
3064 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3065 goto leave_vm_out;
3068 #endif
3069 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
3070 txtdes = mkstemp(tmptxtfile);
3071 chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
3072 if (txtdes < 0) {
3073 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3074 if (!res)
3075 res = ast_waitstream(chan, "");
3076 ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
3077 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3078 goto leave_vm_out;
3081 /* Now play the beep once we have the message number for our next message. */
3082 if (res >= 0) {
3083 /* Unless we're *really* silent, try to send the beep */
3084 res = ast_stream_and_wait(chan, "beep", chan->language, "");
3087 /* Store information */
3088 txt = fdopen(txtdes, "w+");
3089 if (txt) {
3090 get_date(date, sizeof(date));
3091 fprintf(txt,
3092 ";\n"
3093 "; Message Information file\n"
3094 ";\n"
3095 "[message]\n"
3096 "origmailbox=%s\n"
3097 "context=%s\n"
3098 "macrocontext=%s\n"
3099 "exten=%s\n"
3100 "priority=%d\n"
3101 "callerchan=%s\n"
3102 "callerid=%s\n"
3103 "origdate=%s\n"
3104 "origtime=%ld\n"
3105 "category=%s\n",
3106 ext,
3107 chan->context,
3108 chan->macrocontext,
3109 chan->exten,
3110 chan->priority,
3111 chan->name,
3112 ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
3113 date, (long)time(NULL),
3114 category ? category : "");
3115 } else
3116 ast_log(LOG_WARNING, "Error opening text file for output\n");
3117 #ifdef IMAP_STORAGE
3118 res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
3119 #else
3120 res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL);
3121 #endif
3123 if (txt) {
3124 if (duration < vmminmessage) {
3125 fclose(txt);
3126 if (option_verbose > 2)
3127 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
3128 ast_filedelete(tmptxtfile, NULL);
3129 unlink(tmptxtfile);
3130 } else {
3131 fprintf(txt, "duration=%d\n", duration);
3132 fclose(txt);
3133 if (vm_lock_path(dir)) {
3134 ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
3135 /* Delete files */
3136 ast_filedelete(tmptxtfile, NULL);
3137 unlink(tmptxtfile);
3138 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
3139 if (option_debug)
3140 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
3141 unlink(tmptxtfile);
3142 ast_unlock_path(dir);
3143 } else {
3144 for (;;) {
3145 make_file(fn, sizeof(fn), dir, msgnum);
3146 if (!EXISTS(dir, msgnum, fn, NULL))
3147 break;
3148 msgnum++;
3151 /* assign a variable with the name of the voicemail file */
3152 #ifndef IMAP_STORAGE
3153 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3154 #else
3155 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3156 #endif
3158 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3159 ast_filerename(tmptxtfile, fn, NULL);
3160 rename(tmptxtfile, txtfile);
3162 ast_unlock_path(dir);
3163 /* We must store the file first, before copying the message, because
3164 * ODBC storage does the entire copy with SQL.
3166 if (ast_fileexists(fn, NULL, NULL) > 0) {
3167 STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
3170 /* Are there to be more recipients of this message? */
3171 while (tmpptr) {
3172 struct ast_vm_user recipu, *recip;
3173 char *exten, *context;
3175 exten = strsep(&tmpptr, "&");
3176 context = strchr(exten, '@');
3177 if (context) {
3178 *context = '\0';
3179 context++;
3181 if ((recip = find_user(&recipu, context, exten))) {
3182 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
3183 free_user(recip);
3186 /* Notification and disposal needs to happen after the copy, though. */
3187 if (ast_fileexists(fn, NULL, NULL)) {
3188 notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
3189 DISPOSE(dir, msgnum);
3194 if (res == '0') {
3195 goto transfer;
3196 } else if (res > 0)
3197 res = 0;
3199 if (duration < vmminmessage)
3200 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3201 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3202 else
3203 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3204 } else
3205 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3206 leave_vm_out:
3207 free_user(vmu);
3209 return res;
3212 #ifndef IMAP_STORAGE
3213 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3215 /* we know max messages, so stop process when number is hit */
3217 int x,dest;
3218 char sfn[PATH_MAX];
3219 char dfn[PATH_MAX];
3221 if (vm_lock_path(dir))
3222 return ERROR_LOCK_PATH;
3224 for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3225 make_file(sfn, sizeof(sfn), dir, x);
3226 if (EXISTS(dir, x, sfn, NULL)) {
3228 if (x != dest) {
3229 make_file(dfn, sizeof(dfn), dir, dest);
3230 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3233 dest++;
3236 ast_unlock_path(dir);
3238 return 0;
3240 #endif
3242 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3244 int d;
3245 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
3246 return d;
3249 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3251 #ifdef IMAP_STORAGE
3252 /* we must use mbox(x) folder names, and copy the message there */
3253 /* simple. huh? */
3254 long res;
3255 char sequence[10];
3257 /* if save to Old folder, just leave in INBOX */
3258 if (box == 1) return 10;
3259 /* get the real IMAP message number for this message */
3260 snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
3261 if (option_debug > 2)
3262 ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,(char *) mbox(box));
3263 res = mail_copy(vms->mailstream,sequence,(char *) mbox(box));
3264 if (res == 1) return 0;
3265 return 1;
3266 #else
3267 char *dir = vms->curdir;
3268 char *username = vms->username;
3269 char *context = vmu->context;
3270 char sfn[PATH_MAX];
3271 char dfn[PATH_MAX];
3272 char ddir[PATH_MAX];
3273 const char *dbox = mbox(box);
3274 int x;
3275 make_file(sfn, sizeof(sfn), dir, msg);
3276 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
3278 if (vm_lock_path(ddir))
3279 return ERROR_LOCK_PATH;
3281 for (x = 0; x < vmu->maxmsg; x++) {
3282 make_file(dfn, sizeof(dfn), ddir, x);
3283 if (!EXISTS(ddir, x, dfn, NULL))
3284 break;
3286 if (x >= vmu->maxmsg) {
3287 ast_unlock_path(ddir);
3288 return ERROR_MAILBOX_FULL;
3290 if (strcmp(sfn, dfn)) {
3291 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
3293 ast_unlock_path(ddir);
3294 #endif
3295 return 0;
3298 static int adsi_logo(unsigned char *buf)
3300 int bytes = 0;
3301 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
3302 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
3303 return bytes;
3306 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
3308 unsigned char buf[256];
3309 int bytes=0;
3310 int x;
3311 char num[5];
3313 *useadsi = 0;
3314 bytes += ast_adsi_data_mode(buf + bytes);
3315 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3317 bytes = 0;
3318 bytes += adsi_logo(buf);
3319 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3320 #ifdef DISPLAY
3321 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
3322 #endif
3323 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3324 bytes += ast_adsi_data_mode(buf + bytes);
3325 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3327 if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
3328 bytes = 0;
3329 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
3330 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3331 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3332 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3333 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3334 return 0;
3337 #ifdef DISPLAY
3338 /* Add a dot */
3339 bytes = 0;
3340 bytes += ast_adsi_logo(buf);
3341 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3342 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
3343 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3344 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3345 #endif
3346 bytes = 0;
3347 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
3348 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
3349 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
3350 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
3351 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
3352 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
3353 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3355 #ifdef DISPLAY
3356 /* Add another dot */
3357 bytes = 0;
3358 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
3359 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3361 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3362 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3363 #endif
3365 bytes = 0;
3366 /* These buttons we load but don't use yet */
3367 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
3368 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
3369 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
3370 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
3371 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
3372 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
3373 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3375 #ifdef DISPLAY
3376 /* Add another dot */
3377 bytes = 0;
3378 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
3379 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3380 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3381 #endif
3383 bytes = 0;
3384 for (x=0;x<5;x++) {
3385 snprintf(num, sizeof(num), "%d", x);
3386 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
3388 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
3389 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3391 #ifdef DISPLAY
3392 /* Add another dot */
3393 bytes = 0;
3394 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
3395 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3396 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3397 #endif
3399 if (ast_adsi_end_download(chan)) {
3400 bytes = 0;
3401 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
3402 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3403 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3404 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3405 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3406 return 0;
3408 bytes = 0;
3409 bytes += ast_adsi_download_disconnect(buf + bytes);
3410 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3411 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3413 if (option_debug)
3414 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
3416 #ifdef DISPLAY
3417 /* Add last dot */
3418 bytes = 0;
3419 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
3420 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3421 #endif
3422 if (option_debug)
3423 ast_log(LOG_DEBUG, "Restarting session...\n");
3425 bytes = 0;
3426 /* Load the session now */
3427 if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
3428 *useadsi = 1;
3429 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
3430 } else
3431 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
3433 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3434 return 0;
3437 static void adsi_begin(struct ast_channel *chan, int *useadsi)
3439 int x;
3440 if (!ast_adsi_available(chan))
3441 return;
3442 x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
3443 if (x < 0)
3444 return;
3445 if (!x) {
3446 if (adsi_load_vmail(chan, useadsi)) {
3447 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
3448 return;
3450 } else
3451 *useadsi = 1;
3454 static void adsi_login(struct ast_channel *chan)
3456 unsigned char buf[256];
3457 int bytes=0;
3458 unsigned char keys[8];
3459 int x;
3460 if (!ast_adsi_available(chan))
3461 return;
3463 for (x=0;x<8;x++)
3464 keys[x] = 0;
3465 /* Set one key for next */
3466 keys[3] = ADSI_KEY_APPS + 3;
3468 bytes += adsi_logo(buf + bytes);
3469 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
3470 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
3471 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3472 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
3473 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
3474 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
3475 bytes += ast_adsi_set_keys(buf + bytes, keys);
3476 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3477 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3480 static void adsi_password(struct ast_channel *chan)
3482 unsigned char buf[256];
3483 int bytes=0;
3484 unsigned char keys[8];
3485 int x;
3486 if (!ast_adsi_available(chan))
3487 return;
3489 for (x=0;x<8;x++)
3490 keys[x] = 0;
3491 /* Set one key for next */
3492 keys[3] = ADSI_KEY_APPS + 3;
3494 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3495 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
3496 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
3497 bytes += ast_adsi_set_keys(buf + bytes, keys);
3498 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3499 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3502 static void adsi_folders(struct ast_channel *chan, int start, char *label)
3504 unsigned char buf[256];
3505 int bytes=0;
3506 unsigned char keys[8];
3507 int x,y;
3509 if (!ast_adsi_available(chan))
3510 return;
3512 for (x=0;x<5;x++) {
3513 y = ADSI_KEY_APPS + 12 + start + x;
3514 if (y > ADSI_KEY_APPS + 12 + 4)
3515 y = 0;
3516 keys[x] = ADSI_KEY_SKT | y;
3518 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
3519 keys[6] = 0;
3520 keys[7] = 0;
3522 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
3523 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
3524 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3525 bytes += ast_adsi_set_keys(buf + bytes, keys);
3526 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3528 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3531 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
3533 int bytes=0;
3534 unsigned char buf[256];
3535 char buf1[256], buf2[256];
3536 char fn2[PATH_MAX];
3538 char cid[256]="";
3539 char *val;
3540 char *name, *num;
3541 char datetime[21]="";
3542 FILE *f;
3544 unsigned char keys[8];
3546 int x;
3548 if (!ast_adsi_available(chan))
3549 return;
3551 /* Retrieve important info */
3552 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
3553 f = fopen(fn2, "r");
3554 if (f) {
3555 while (!feof(f)) {
3556 fgets((char *)buf, sizeof(buf), f);
3557 if (!feof(f)) {
3558 char *stringp=NULL;
3559 stringp = (char *)buf;
3560 strsep(&stringp, "=");
3561 val = strsep(&stringp, "=");
3562 if (!ast_strlen_zero(val)) {
3563 if (!strcmp((char *)buf, "callerid"))
3564 ast_copy_string(cid, val, sizeof(cid));
3565 if (!strcmp((char *)buf, "origdate"))
3566 ast_copy_string(datetime, val, sizeof(datetime));
3570 fclose(f);
3572 /* New meaning for keys */
3573 for (x=0;x<5;x++)
3574 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3575 keys[6] = 0x0;
3576 keys[7] = 0x0;
3578 if (!vms->curmsg) {
3579 /* No prev key, provide "Folder" instead */
3580 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3582 if (vms->curmsg >= vms->lastmsg) {
3583 /* If last message ... */
3584 if (vms->curmsg) {
3585 /* but not only message, provide "Folder" instead */
3586 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3587 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3589 } else {
3590 /* Otherwise if only message, leave blank */
3591 keys[3] = 1;
3595 if (!ast_strlen_zero(cid)) {
3596 ast_callerid_parse(cid, &name, &num);
3597 if (!name)
3598 name = num;
3599 } else
3600 name = "Unknown Caller";
3602 /* If deleted, show "undeleted" */
3604 if (vms->deleted[vms->curmsg])
3605 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3607 /* Except "Exit" */
3608 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3609 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
3610 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
3611 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
3613 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3614 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3615 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
3616 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
3617 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3618 bytes += ast_adsi_set_keys(buf + bytes, keys);
3619 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3621 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3624 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
3626 int bytes=0;
3627 unsigned char buf[256];
3628 unsigned char keys[8];
3630 int x;
3632 if (!ast_adsi_available(chan))
3633 return;
3635 /* New meaning for keys */
3636 for (x=0;x<5;x++)
3637 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3639 keys[6] = 0x0;
3640 keys[7] = 0x0;
3642 if (!vms->curmsg) {
3643 /* No prev key, provide "Folder" instead */
3644 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3646 if (vms->curmsg >= vms->lastmsg) {
3647 /* If last message ... */
3648 if (vms->curmsg) {
3649 /* but not only message, provide "Folder" instead */
3650 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3651 } else {
3652 /* Otherwise if only message, leave blank */
3653 keys[3] = 1;
3657 /* If deleted, show "undeleted" */
3658 if (vms->deleted[vms->curmsg])
3659 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3661 /* Except "Exit" */
3662 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3663 bytes += ast_adsi_set_keys(buf + bytes, keys);
3664 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3666 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3669 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
3671 unsigned char buf[256] = "";
3672 char buf1[256] = "", buf2[256] = "";
3673 int bytes=0;
3674 unsigned char keys[8];
3675 int x;
3677 char *newm = (vms->newmessages == 1) ? "message" : "messages";
3678 char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
3679 if (!ast_adsi_available(chan))
3680 return;
3681 if (vms->newmessages) {
3682 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
3683 if (vms->oldmessages) {
3684 strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
3685 snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
3686 } else {
3687 snprintf(buf2, sizeof(buf2), "%s.", newm);
3689 } else if (vms->oldmessages) {
3690 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
3691 snprintf(buf2, sizeof(buf2), "%s.", oldm);
3692 } else {
3693 strcpy(buf1, "You have no messages.");
3694 buf2[0] = ' ';
3695 buf2[1] = '\0';
3697 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3698 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3699 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3701 for (x=0;x<6;x++)
3702 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3703 keys[6] = 0;
3704 keys[7] = 0;
3706 /* Don't let them listen if there are none */
3707 if (vms->lastmsg < 0)
3708 keys[0] = 1;
3709 bytes += ast_adsi_set_keys(buf + bytes, keys);
3711 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3713 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3716 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
3718 unsigned char buf[256] = "";
3719 char buf1[256] = "", buf2[256] = "";
3720 int bytes=0;
3721 unsigned char keys[8];
3722 int x;
3724 char *mess = (vms->lastmsg == 0) ? "message" : "messages";
3726 if (!ast_adsi_available(chan))
3727 return;
3729 /* Original command keys */
3730 for (x=0;x<6;x++)
3731 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3733 keys[6] = 0;
3734 keys[7] = 0;
3736 if ((vms->lastmsg + 1) < 1)
3737 keys[0] = 0;
3739 snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
3740 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
3742 if (vms->lastmsg + 1)
3743 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
3744 else
3745 strcpy(buf2, "no messages.");
3746 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3747 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3748 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
3749 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3750 bytes += ast_adsi_set_keys(buf + bytes, keys);
3752 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3754 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3759 static void adsi_clear(struct ast_channel *chan)
3761 char buf[256];
3762 int bytes=0;
3763 if (!ast_adsi_available(chan))
3764 return;
3765 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3766 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3768 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3772 static void adsi_goodbye(struct ast_channel *chan)
3774 unsigned char buf[256];
3775 int bytes=0;
3777 if (!ast_adsi_available(chan))
3778 return;
3779 bytes += adsi_logo(buf + bytes);
3780 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
3781 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
3782 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3783 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3785 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3788 /*--- get_folder: Folder menu ---*/
3789 /* Plays "press 1 for INBOX messages" etc
3790 Should possibly be internationalized
3792 static int get_folder(struct ast_channel *chan, int start)
3794 int x;
3795 int d;
3796 char fn[PATH_MAX];
3797 d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
3798 if (d)
3799 return d;
3800 for (x = start; x< 5; x++) { /* For all folders */
3801 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
3802 return d;
3803 d = ast_play_and_wait(chan, "vm-for"); /* "for" */
3804 if (d)
3805 return d;
3806 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
3807 d = vm_play_folder_name(chan, fn);
3808 if (d)
3809 return d;
3810 d = ast_waitfordigit(chan, 500);
3811 if (d)
3812 return d;
3814 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
3815 if (d)
3816 return d;
3817 d = ast_waitfordigit(chan, 4000);
3818 return d;
3821 static int get_folder2(struct ast_channel *chan, char *fn, int start)
3823 int res = 0;
3824 res = ast_play_and_wait(chan, fn); /* Folder name */
3825 while (((res < '0') || (res > '9')) &&
3826 (res != '#') && (res >= 0)) {
3827 res = get_folder(chan, 0);
3829 return res;
3832 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfmts,
3833 char *context, signed char record_gain, long *duration, struct vm_state *vms)
3835 int cmd = 0;
3836 int retries = 0, prepend_duration = 0, already_recorded = 0;
3837 signed char zero_gain = 0;
3838 struct ast_config *msg_cfg;
3839 const char *duration_str;
3840 char msgfile[PATH_MAX], backup[PATH_MAX];
3841 char textfile[PATH_MAX];
3843 /* Must always populate duration correctly */
3844 make_file(msgfile, sizeof(msgfile), curdir, curmsg);
3845 strcpy(textfile, msgfile);
3846 strcpy(backup, msgfile);
3847 strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
3848 strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
3850 if (!(msg_cfg = ast_config_load(textfile))) {
3851 return -1;
3854 *duration = 0;
3855 if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
3856 *duration = atoi(duration_str);
3858 while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
3859 if (cmd)
3860 retries = 0;
3861 switch (cmd) {
3862 case '1':
3863 /* prepend a message to the current message, update the metadata and return */
3865 prepend_duration = 0;
3867 /* if we can't read the message metadata, stop now */
3868 if (!msg_cfg) {
3869 cmd = 0;
3870 break;
3873 /* Back up the original file, so we can retry the prepend */
3874 if (already_recorded)
3875 ast_filecopy(backup, msgfile, NULL);
3876 else
3877 ast_filecopy(msgfile, backup, NULL);
3878 already_recorded = 1;
3880 if (record_gain)
3881 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
3883 cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vmfmts, &prepend_duration, 1, silencethreshold, maxsilence);
3884 if (record_gain)
3885 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
3887 if (prepend_duration) {
3888 struct ast_category *msg_cat;
3889 /* need enough space for a maximum-length message duration */
3890 char duration_str[12];
3892 prepend_duration += *duration;
3893 msg_cat = ast_category_get(msg_cfg, "message");
3894 snprintf(duration_str, 11, "%d", prepend_duration);
3895 if (!ast_variable_update(msg_cat, "duration", duration_str, NULL, 0)) {
3896 config_text_file_save(textfile, msg_cfg, "app_voicemail");
3897 STORE(curdir, vmu->mailbox, context, curmsg, chan, vmu, vmfmts, prepend_duration, vms);
3901 break;
3903 case '2':
3904 cmd = 't';
3905 break;
3906 case '*':
3907 cmd = '*';
3908 break;
3909 default:
3910 cmd = ast_play_and_wait(chan,"vm-forwardoptions");
3911 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
3912 if (!cmd)
3913 cmd = ast_play_and_wait(chan,"vm-starmain");
3914 /* "press star to return to the main menu" */
3915 if (!cmd)
3916 cmd = ast_waitfordigit(chan,6000);
3917 if (!cmd)
3918 retries++;
3919 if (retries > 3)
3920 cmd = 't';
3924 ast_config_destroy(msg_cfg);
3925 if (already_recorded)
3926 ast_filedelete(backup, NULL);
3927 if (prepend_duration)
3928 *duration = prepend_duration;
3930 if (cmd == 't' || cmd == 'S')
3931 cmd = 0;
3932 return cmd;
3935 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
3937 char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
3938 int newmsgs = 0, oldmsgs = 0;
3939 const char *category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3941 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
3942 make_file(fn, sizeof(fn), todir, msgnum);
3943 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
3945 if (!ast_strlen_zero(vmu->attachfmt)) {
3946 if (strstr(fmt, vmu->attachfmt)) {
3947 fmt = vmu->attachfmt;
3948 } else {
3949 ast_log(LOG_WARNING, "Attachment format '%s' is not one of the recorded formats '%s'. Falling back to default format for '%s@%s'.\n", vmu->attachfmt, fmt, vmu->mailbox, vmu->context);
3953 /* Attach only the first format */
3954 fmt = ast_strdupa(fmt);
3955 stringp = fmt;
3956 strsep(&stringp, "|");
3958 if (!ast_strlen_zero(vmu->email)) {
3959 int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
3960 char *myserveremail = serveremail;
3961 attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
3962 if (!ast_strlen_zero(vmu->serveremail))
3963 myserveremail = vmu->serveremail;
3965 if (attach_user_voicemail)
3966 RETRIEVE(todir, msgnum);
3968 /*XXX possible imap issue, should category be NULL XXX*/
3969 sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail, chan, category);
3971 if (attach_user_voicemail)
3972 DISPOSE(todir, msgnum);
3975 if (!ast_strlen_zero(vmu->pager)) {
3976 char *myserveremail = serveremail;
3977 if (!ast_strlen_zero(vmu->serveremail))
3978 myserveremail = vmu->serveremail;
3979 sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, duration, vmu, category);
3982 if (ast_test_flag(vmu, VM_DELETE)) {
3983 DELETE(todir, msgnum, fn);
3986 #ifdef IMAP_STORAGE
3987 DELETE(todir, msgnum, fn);
3988 #endif
3989 /* Leave voicemail for someone */
3990 if (ast_app_has_voicemail(ext_context, NULL)) {
3991 ast_app_inboxcount(ext_context, &newmsgs, &oldmsgs);
3993 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\nNew: %d\r\nOld: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs);
3994 run_externnotify(vmu->context, vmu->mailbox);
3995 return 0;
3998 static int forward_message(struct ast_channel *chan, char *context, struct vm_state *vms, struct ast_vm_user *sender, char *fmt, int flag, signed char record_gain)
4000 #ifdef IMAP_STORAGE
4001 BODY *body;
4002 char *header_content;
4003 char *temp;
4004 char todir[256];
4005 int todircount=0;
4006 struct vm_state *dstvms;
4007 #endif
4008 char username[70]="";
4009 int res = 0, cmd = 0;
4010 struct ast_vm_user *receiver = NULL, *vmtmp;
4011 AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
4012 char *stringp;
4013 const char *s;
4014 int saved_messages = 0, found = 0;
4015 int valid_extensions = 0;
4016 char *dir;
4017 int curmsg;
4019 if (vms == NULL) return -1;
4020 dir = vms->curdir;
4021 curmsg = vms->curmsg;
4023 while (!res && !valid_extensions) {
4024 int use_directory = 0;
4025 if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
4026 int done = 0;
4027 int retries = 0;
4028 cmd=0;
4029 while ((cmd >= 0) && !done ){
4030 if (cmd)
4031 retries = 0;
4032 switch (cmd) {
4033 case '1':
4034 use_directory = 0;
4035 done = 1;
4036 break;
4037 case '2':
4038 use_directory = 1;
4039 done=1;
4040 break;
4041 case '*':
4042 cmd = 't';
4043 done = 1;
4044 break;
4045 default:
4046 /* Press 1 to enter an extension press 2 to use the directory */
4047 cmd = ast_play_and_wait(chan,"vm-forward");
4048 if (!cmd)
4049 cmd = ast_waitfordigit(chan,3000);
4050 if (!cmd)
4051 retries++;
4052 if (retries > 3)
4054 cmd = 't';
4055 done = 1;
4060 if (cmd < 0 || cmd == 't')
4061 break;
4064 if (use_directory) {
4065 /* use app_directory */
4067 char old_context[sizeof(chan->context)];
4068 char old_exten[sizeof(chan->exten)];
4069 int old_priority;
4070 struct ast_app* app;
4073 app = pbx_findapp("Directory");
4074 if (app) {
4075 char vmcontext[256];
4076 /* make backup copies */
4077 memcpy(old_context, chan->context, sizeof(chan->context));
4078 memcpy(old_exten, chan->exten, sizeof(chan->exten));
4079 old_priority = chan->priority;
4081 /* call the the Directory, changes the channel */
4082 snprintf(vmcontext, sizeof(vmcontext), "%s||v", context ? context : "default");
4083 res = pbx_exec(chan, app, vmcontext);
4085 ast_copy_string(username, chan->exten, sizeof(username));
4087 /* restore the old context, exten, and priority */
4088 memcpy(chan->context, old_context, sizeof(chan->context));
4089 memcpy(chan->exten, old_exten, sizeof(chan->exten));
4090 chan->priority = old_priority;
4092 } else {
4093 ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
4094 ast_clear_flag((&globalflags), VM_DIRECFORWARD);
4096 } else {
4097 /* Ask for an extension */
4098 res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
4099 if (res)
4100 break;
4101 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
4102 break;
4105 /* start all over if no username */
4106 if (ast_strlen_zero(username))
4107 continue;
4108 stringp = username;
4109 s = strsep(&stringp, "*");
4110 /* start optimistic */
4111 valid_extensions = 1;
4112 while (s) {
4113 /* Don't forward to ourselves but allow leaving a message for ourselves (flag == 1). find_user is going to malloc since we have a NULL as first argument */
4114 if ((flag == 1 || strcmp(s,sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
4115 AST_LIST_INSERT_HEAD(&extensions, receiver, list);
4116 found++;
4117 } else {
4118 valid_extensions = 0;
4119 break;
4121 s = strsep(&stringp, "*");
4123 /* break from the loop of reading the extensions */
4124 if (valid_extensions)
4125 break;
4126 /* "I am sorry, that's not a valid extension. Please try again." */
4127 res = ast_play_and_wait(chan, "pbx-invalid");
4129 /* check if we're clear to proceed */
4130 if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
4131 return res;
4132 if (flag==1) {
4133 struct leave_vm_options leave_options;
4134 char mailbox[AST_MAX_EXTENSION * 2 + 2];
4135 /* Make sure that context doesn't get set as a literal "(null)" (or else find_user won't find it) */
4136 if (context)
4137 snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
4138 else
4139 ast_copy_string(mailbox, username, sizeof(mailbox));
4141 /* Send VoiceMail */
4142 memset(&leave_options, 0, sizeof(leave_options));
4143 leave_options.record_gain = record_gain;
4144 cmd = leave_voicemail(chan, mailbox, &leave_options);
4145 } else {
4146 /* Forward VoiceMail */
4147 long duration = 0;
4148 char origmsgfile[PATH_MAX], msgfile[PATH_MAX];
4149 struct vm_state vmstmp;
4151 memcpy(&vmstmp, vms, sizeof(vmstmp));
4153 make_file(origmsgfile, sizeof(origmsgfile), dir, curmsg);
4154 create_dirpath(vmstmp.curdir, sizeof(vmstmp.curdir), sender->context, vmstmp.username, "tmp");
4155 make_file(msgfile, sizeof(msgfile), vmstmp.curdir, curmsg);
4157 RETRIEVE(dir, curmsg);
4159 /* Alter a surrogate file, only */
4160 copy_plain_file(origmsgfile, msgfile);
4162 cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp);
4163 if (!cmd) {
4164 AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
4165 #ifdef IMAP_STORAGE
4166 char *myserveremail;
4167 int attach_user_voicemail;
4168 /* Need to get message content */
4169 if (option_debug > 2)
4170 ast_log (LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", vms->curmsg, vms->msgArray[vms->curmsg]);
4171 if (vms->msgArray[vms->curmsg] == 0) {
4172 ast_log (LOG_WARNING,"Trying to access unknown message\n");
4173 return -1;
4176 /* This will only work for new messages... */
4177 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
4178 /* empty string means no valid header */
4179 if (ast_strlen_zero(header_content)) {
4180 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
4181 return -1;
4183 /* Get header info needed by sendmail */
4184 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:");
4185 if (temp)
4186 duration = atoi(temp);
4187 else
4188 duration = 0;
4190 /* Attach only the first format */
4191 fmt = ast_strdupa(fmt);
4192 if (fmt) {
4193 stringp = fmt;
4194 strsep(&stringp, "|");
4195 } else {
4196 ast_log (LOG_ERROR,"audio format not set. Default to WAV\n");
4197 fmt = "WAV";
4199 if (!strcasecmp(fmt, "wav49"))
4200 fmt = "WAV";
4201 if (option_debug > 2)
4202 ast_log (LOG_DEBUG,"**** format set to %s, vmfmts set to %s\n",fmt,vmfmts);
4203 /* ast_copy_string(fmt, vmfmts, sizeof(fmt));*/
4204 /* if (!ast_strlen_zero(fmt)) { */
4205 snprintf(todir, sizeof(todir), "%s%s/%s/tmp", VM_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
4206 make_gsm_file(vms->fn, sizeof(vms->fn), vms->imapuser, todir, vms->curmsg);
4207 if (option_debug > 2)
4208 ast_log (LOG_DEBUG,"Before mail_fetchstructure, message number is %ld, filename is:%s\n",vms->msgArray[vms->curmsg], vms->fn);
4209 /*mail_fetchstructure (mailstream, vmArray[0], &body); */
4210 mail_fetchstructure (vms->mailstream, vms->msgArray[vms->curmsg], &body);
4211 save_body(body,vms,"3","gsm");
4212 /* should not assume "fmt" here! */
4213 save_body(body,vms,"2",fmt);
4215 /* get destination mailbox */
4216 dstvms = get_vm_state_by_mailbox(vmtmp->mailbox,0);
4217 if (dstvms) {
4218 init_mailstream(dstvms, 0);
4219 if (!dstvms->mailstream) {
4220 ast_log (LOG_ERROR,"IMAP mailstream for %s is NULL\n",vmtmp->mailbox);
4221 } else {
4222 STORE(todir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms);
4223 run_externnotify(vmtmp->context, vmtmp->mailbox);
4225 } else {
4226 ast_log (LOG_ERROR,"Could not find state information for mailbox %s\n",vmtmp->mailbox);
4229 myserveremail = serveremail;
4230 if (!ast_strlen_zero(vmtmp->serveremail))
4231 myserveremail = vmtmp->serveremail;
4232 attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
4233 attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
4234 /* NULL category for IMAP storage */
4235 sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), vms->fn, fmt, duration, attach_user_voicemail, chan, NULL);
4236 #else
4237 copy_message(chan, sender, -1, curmsg, duration, vmtmp, fmt, vmstmp.curdir);
4238 #endif
4239 saved_messages++;
4240 AST_LIST_REMOVE_CURRENT(&extensions, list);
4241 free_user(vmtmp);
4242 if (res)
4243 break;
4245 AST_LIST_TRAVERSE_SAFE_END;
4246 if (saved_messages > 0) {
4247 /* give confirmation that the message was saved */
4248 /* commented out since we can't forward batches yet
4249 if (saved_messages == 1)
4250 res = ast_play_and_wait(chan, "vm-message");
4251 else
4252 res = ast_play_and_wait(chan, "vm-messages");
4253 if (!res)
4254 res = ast_play_and_wait(chan, "vm-saved"); */
4255 res = ast_play_and_wait(chan, "vm-msgsaved");
4259 /* Remove surrogate file */
4260 vm_delete(msgfile);
4263 /* If anything failed above, we still have this list to free */
4264 while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list)))
4265 free_user(vmtmp);
4266 return res ? res : cmd;
4269 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
4271 int res;
4272 if ((res = ast_stream_and_wait(chan, file, chan->language, AST_DIGIT_ANY)) < 0)
4273 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
4274 return res;
4277 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
4279 return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", "2", skipms);
4282 static int play_message_category(struct ast_channel *chan, const char *category)
4284 int res = 0;
4286 if (!ast_strlen_zero(category))
4287 res = ast_play_and_wait(chan, category);
4289 if (res) {
4290 ast_log(LOG_WARNING, "No sound file for category '%s' was found.\n", category);
4291 res = 0;
4294 return res;
4297 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
4299 int res = 0;
4300 struct vm_zone *the_zone = NULL;
4301 time_t t;
4303 if (ast_get_time_t(origtime, &t, 0, NULL)) {
4304 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
4305 return 0;
4308 /* Does this user have a timezone specified? */
4309 if (!ast_strlen_zero(vmu->zonetag)) {
4310 /* Find the zone in the list */
4311 struct vm_zone *z;
4312 AST_LIST_LOCK(&zones);
4313 AST_LIST_TRAVERSE(&zones, z, list) {
4314 if (!strcmp(z->name, vmu->zonetag)) {
4315 the_zone = z;
4316 break;
4319 AST_LIST_UNLOCK(&zones);
4322 /* No internal variable parsing for now, so we'll comment it out for the time being */
4323 #if 0
4324 /* Set the DIFF_* variables */
4325 ast_localtime(&t, &time_now, NULL);
4326 tv_now = ast_tvnow();
4327 tnow = tv_now.tv_sec;
4328 ast_localtime(&tnow, &time_then, NULL);
4330 /* Day difference */
4331 if (time_now.tm_year == time_then.tm_year)
4332 snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
4333 else
4334 snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
4335 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
4337 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
4338 #endif
4339 if (the_zone)
4340 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
4341 else if (!strcasecmp(chan->language,"pl")) /* POLISH syntax */
4342 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
4343 else if (!strcasecmp(chan->language,"se")) /* SWEDISH syntax */
4344 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
4345 else if (!strcasecmp(chan->language,"no")) /* NORWEGIAN syntax */
4346 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
4347 else if (!strcasecmp(chan->language,"de")) /* GERMAN syntax */
4348 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
4349 else if (!strcasecmp(chan->language,"nl")) /* DUTCH syntax */
4350 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
4351 else if (!strcasecmp(chan->language,"it")) /* ITALIAN syntax */
4352 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL);
4353 else if (!strcasecmp(chan->language,"gr"))
4354 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q H 'digits/kai' M ", NULL);
4355 else if (!strcasecmp(chan->language,"pt_BR"))
4356 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/pt-as' HM ", NULL);
4357 else
4358 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
4359 #if 0
4360 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
4361 #endif
4362 return res;
4367 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
4369 int res = 0;
4370 int i;
4371 char *callerid, *name;
4372 char prefile[PATH_MAX] = "";
4375 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
4376 /* BB: Still need to change this so that if this function is called by the message envelope (and someone is explicitly requesting to hear the CID), it does not check to see if CID is enabled in the config file */
4377 if ((cid == NULL)||(context == NULL))
4378 return res;
4380 /* Strip off caller ID number from name */
4381 if (option_debug > 2)
4382 ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
4383 ast_callerid_parse(cid, &name, &callerid);
4384 if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
4385 /* Check for internal contexts and only */
4386 /* say extension when the call didn't come from an internal context in the list */
4387 for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
4388 if (option_debug > 2)
4389 ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
4390 if ((strcmp(cidinternalcontexts[i], context) == 0))
4391 break;
4393 if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
4394 if (!res) {
4395 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
4396 if (!ast_strlen_zero(prefile)) {
4397 /* See if we can find a recorded name for this person instead of their extension number */
4398 if (ast_fileexists(prefile, NULL, NULL) > 0) {
4399 if (option_verbose > 2)
4400 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
4401 if (!callback)
4402 res = wait_file2(chan, vms, "vm-from");
4403 res = ast_stream_and_wait(chan, prefile, chan->language, "");
4404 } else {
4405 if (option_verbose > 2)
4406 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
4407 /* BB: Say "from extension" as one saying to sound smoother */
4408 if (!callback)
4409 res = wait_file2(chan, vms, "vm-from-extension");
4410 res = ast_say_digit_str(chan, callerid, "", chan->language);
4416 else if (!res){
4417 if (option_debug > 2)
4418 ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
4419 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
4420 if (!callback)
4421 res = wait_file2(chan, vms, "vm-from-phonenumber");
4422 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
4424 } else {
4425 /* Number unknown */
4426 if (option_debug)
4427 ast_log(LOG_DEBUG, "VM-CID: From an unknown number\n");
4428 /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
4429 res = wait_file2(chan, vms, "vm-unknown-caller");
4431 return res;
4434 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
4436 int res = 0;
4437 int durationm;
4438 int durations;
4439 /* Verify that we have a duration for the message */
4440 if (duration == NULL)
4441 return res;
4443 /* Convert from seconds to minutes */
4444 durations=atoi(duration);
4445 durationm=(durations / 60);
4447 if (option_debug > 2)
4448 ast_log(LOG_DEBUG, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
4450 if ((!res) && (durationm >= minduration)) {
4451 res = wait_file2(chan, vms, "vm-duration");
4453 /* POLISH syntax */
4454 if (!strcasecmp(chan->language, "pl")) {
4455 div_t num = div(durationm, 10);
4457 if (durationm == 1) {
4458 res = ast_play_and_wait(chan, "digits/1z");
4459 res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
4460 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
4461 if (num.rem == 2) {
4462 if (!num.quot) {
4463 res = ast_play_and_wait(chan, "digits/2-ie");
4464 } else {
4465 res = say_and_wait(chan, durationm - 2 , chan->language);
4466 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
4468 } else {
4469 res = say_and_wait(chan, durationm, chan->language);
4471 res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
4472 } else {
4473 res = say_and_wait(chan, durationm, chan->language);
4474 res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
4476 /* DEFAULT syntax */
4477 } else {
4478 res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
4479 res = wait_file2(chan, vms, "vm-minutes");
4482 return res;
4485 #ifdef IMAP_STORAGE
4486 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
4488 BODY *body;
4489 char *header_content;
4490 char cid[256];
4491 char context[256];
4492 char origtime[32];
4493 char duration[16];
4494 char category[32];
4495 char todir[PATH_MAX];
4496 int res = 0;
4497 char *attachedfilefmt;
4498 char *temp;
4500 vms->starting = 0;
4501 if (option_debug > 2)
4502 ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
4503 if (vms->msgArray[vms->curmsg] == 0) {
4504 ast_log (LOG_WARNING,"Trying to access unknown message\n");
4505 return -1;
4508 /* This will only work for new messages... */
4509 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
4510 /* empty string means no valid header */
4511 if (ast_strlen_zero(header_content)) {
4512 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
4513 return -1;
4515 snprintf(todir, sizeof(todir), "%s%s/%s/tmp", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
4516 make_gsm_file(vms->fn, sizeof(vms->fn), vms->imapuser, todir, vms->curmsg);
4518 mail_fetchstructure (vms->mailstream,vms->msgArray[vms->curmsg],&body);
4520 /* We have the body, now we extract the file name of the first attachment. */
4521 if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
4522 attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
4523 } else {
4524 ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
4525 return -1;
4528 /* Find the format of the attached file */
4530 strsep(&attachedfilefmt, ".");
4531 if (!attachedfilefmt) {
4532 ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
4533 return -1;
4535 save_body(body, vms, "2", attachedfilefmt);
4537 adsi_message(chan, vms);
4538 if (!vms->curmsg)
4539 res = wait_file2(chan, vms, "vm-first"); /* "First" */
4540 else if (vms->curmsg == vms->lastmsg)
4541 res = wait_file2(chan, vms, "vm-last"); /* "last" */
4542 if (!res) {
4543 res = wait_file2(chan, vms, "vm-message"); /* "message" */
4544 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
4545 if (!res)
4546 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
4550 /* Get info from headers!! */
4551 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:");
4553 if (temp)
4554 ast_copy_string(cid, temp, sizeof(cid));
4555 else
4556 cid[0] = '\0';
4558 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Context:");
4560 if (temp)
4561 ast_copy_string(context, temp, sizeof(context));
4562 else
4563 context[0] = '\0';
4565 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:");
4567 if (temp)
4568 ast_copy_string(origtime, temp, sizeof(origtime));
4569 else
4570 origtime[0] = '\0';
4572 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:");
4574 if (temp)
4575 ast_copy_string(duration,temp, sizeof(duration));
4576 else
4577 duration[0] = '\0';
4579 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Category:");
4581 if (temp)
4582 ast_copy_string(category,temp, sizeof(category));
4583 else
4584 category[0] = '\0';
4586 /*if (!strncasecmp("macro",context,5)) Macro names in contexts are useless for our needs */
4587 /* context = ast_variable_retrieve(msg_cfg, "message","macrocontext"); */
4588 if (res == '1')
4589 res = 0;
4591 if ((!res) && !ast_strlen_zero(category)) {
4592 res = play_message_category(chan, category);
4595 if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)) && origtime[0] != '\0')
4596 res = play_message_datetime(chan, vmu, origtime, "IMAP_STORAGE");
4597 if ((!res) && (ast_test_flag(vmu, VM_SAYCID)) && cid[0] !='\0' && context[0] !='\0')
4598 res = play_message_callerid(chan, vms, cid, context, 0);
4600 if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)) && duration[0] != '\0')
4601 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
4603 /* Allow pressing '1' to skip envelope / callerid */
4604 /* if (res == '1')
4605 res = 0;
4607 /*ast_config_destroy(msg_cfg);*/
4608 res = 0;
4610 if (!res) {
4611 vms->heard[vms->curmsg] = 1;
4612 res = wait_file(chan, vms, vms->fn);
4614 DISPOSE(vms->curdir, vms->curmsg);
4615 DELETE(0, 0, vms->fn);
4616 return res;
4618 #else
4619 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
4621 int res = 0;
4622 char filename[256], *cid;
4623 const char *origtime, *context, *category, *duration;
4624 struct ast_config *msg_cfg;
4626 vms->starting = 0;
4627 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
4628 adsi_message(chan, vms);
4629 if (!vms->curmsg)
4630 res = wait_file2(chan, vms, "vm-first"); /* "First" */
4631 else if (vms->curmsg == vms->lastmsg)
4632 res = wait_file2(chan, vms, "vm-last"); /* "last" */
4633 if (!res) {
4634 /* POLISH syntax */
4635 if (!strcasecmp(chan->language, "pl")) {
4636 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
4637 int ten, one;
4638 char nextmsg[256];
4639 ten = (vms->curmsg + 1) / 10;
4640 one = (vms->curmsg + 1) % 10;
4642 if (vms->curmsg < 20) {
4643 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
4644 res = wait_file2(chan, vms, nextmsg);
4645 } else {
4646 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
4647 res = wait_file2(chan, vms, nextmsg);
4648 if (one > 0) {
4649 if (!res) {
4650 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
4651 res = wait_file2(chan, vms, nextmsg);
4656 if (!res)
4657 res = wait_file2(chan, vms, "vm-message");
4658 } else {
4659 if (!strcasecmp(chan->language, "se")) /* SWEDISH syntax */
4660 res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
4661 else /* DEFAULT syntax */
4662 res = wait_file2(chan, vms, "vm-message");
4663 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
4664 if (!res)
4665 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
4670 /* Retrieve info from VM attribute file */
4671 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
4672 snprintf(filename, sizeof(filename), "%s.txt", vms->fn2);
4673 RETRIEVE(vms->curdir, vms->curmsg);
4674 msg_cfg = ast_config_load(filename);
4675 if (!msg_cfg) {
4676 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
4677 return 0;
4680 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
4681 ast_log(LOG_WARNING, "No origtime?!\n");
4682 DISPOSE(vms->curdir, vms->curmsg);
4683 ast_config_destroy(msg_cfg);
4684 return 0;
4687 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
4688 duration = ast_variable_retrieve(msg_cfg, "message", "duration");
4689 category = ast_variable_retrieve(msg_cfg, "message", "category");
4691 context = ast_variable_retrieve(msg_cfg, "message", "context");
4692 if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
4693 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
4694 if (!res)
4695 res = play_message_category(chan, category);
4696 if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
4697 res = play_message_datetime(chan, vmu, origtime, filename);
4698 if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
4699 res = play_message_callerid(chan, vms, cid, context, 0);
4700 if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
4701 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
4702 /* Allow pressing '1' to skip envelope / callerid */
4703 if (res == '1')
4704 res = 0;
4705 ast_config_destroy(msg_cfg);
4707 if (!res) {
4708 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
4709 vms->heard[vms->curmsg] = 1;
4710 if ((res = wait_file(chan, vms, vms->fn)) < 0) {
4711 ast_log(LOG_WARNING, "Playback of message %s failed\n", vms->fn);
4712 res = 0;
4715 DISPOSE(vms->curdir, vms->curmsg);
4716 return res;
4718 #endif
4720 #ifdef IMAP_STORAGE
4721 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
4723 char tmp[256], *t = tmp;
4724 size_t left = sizeof(tmp);
4726 if (box == 1) {
4727 ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
4728 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(1));
4729 } else {
4730 ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
4731 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
4734 /* Build up server information */
4735 ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
4737 /* Add authentication user if present */
4738 if (!ast_strlen_zero(authuser))
4739 ast_build_string(&t, &left, "/authuser=%s", authuser);
4741 /* Add flags if present */
4742 if (!ast_strlen_zero(imapflags))
4743 ast_build_string(&t, &left, "/%s", imapflags);
4745 /* End with username */
4746 ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
4748 if (box == 0 || box == 1)
4749 snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
4750 else
4751 snprintf(spec, len, "%s%s%c%s", tmp, imapfolder, delimiter, mbox(box));
4754 static int init_mailstream(struct vm_state *vms, int box)
4756 MAILSTREAM *stream = NIL;
4757 long debug;
4758 char tmp[256];
4760 if (!vms) {
4761 ast_log (LOG_ERROR,"vm_state is NULL!\n");
4762 return -1;
4764 if (option_debug > 2)
4765 ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
4766 if (vms->mailstream == NIL || !vms->mailstream) {
4767 if (option_debug)
4768 ast_log (LOG_DEBUG,"mailstream not set.\n");
4769 } else {
4770 stream = vms->mailstream;
4772 /* debug = T; user wants protocol telemetry? */
4773 debug = NIL; /* NO protocol telemetry? */
4775 if (delimiter == '\0') { /* did not probe the server yet */
4776 char *cp;
4777 #ifdef USE_SYSTEM_IMAP
4778 #include <imap/linkage.c>
4779 #elif defined(USE_SYSTEM_CCLIENT)
4780 #include <c-client/linkage.c>
4781 #else
4782 #include "linkage.c"
4783 #endif
4784 /* Connect to INBOX first to get folders delimiter */
4785 imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
4786 ast_mutex_lock(&vms->lock);
4787 stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
4788 ast_mutex_unlock(&vms->lock);
4789 if (stream == NIL) {
4790 ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
4791 return -1;
4793 get_mailbox_delimiter(stream);
4794 /* update delimiter in imapfolder */
4795 for (cp = imapfolder; *cp; cp++)
4796 if (*cp == '/')
4797 *cp = delimiter;
4799 /* Now connect to the target folder */
4800 imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
4801 if (option_debug > 2)
4802 ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
4803 ast_mutex_lock(&vms->lock);
4804 vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
4805 ast_mutex_unlock(&vms->lock);
4806 if (vms->mailstream == NIL) {
4807 return -1;
4808 } else {
4809 return 0;
4813 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
4815 SEARCHPGM *pgm;
4816 SEARCHHEADER *hdr;
4817 int ret;
4819 ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
4820 if (option_debug > 2)
4821 ast_log(LOG_DEBUG,"Before init_mailstream, user is %s\n",vmu->imapuser);
4822 ret = init_mailstream(vms, box);
4823 if (ret != 0 || !vms->mailstream) {
4824 ast_log (LOG_ERROR,"Could not initialize mailstream\n");
4825 return -1;
4828 /* Check Quota */
4829 if (box == 0) {
4830 if (option_debug > 2)
4831 ast_log(LOG_DEBUG, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
4832 check_quota(vms,(char *)mbox(box));
4835 pgm = mail_newsearchpgm();
4837 /* Check IMAP folder for Asterisk messages only... */
4838 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", vmu->mailbox);
4839 pgm->header = hdr;
4840 pgm->deleted = 0;
4841 pgm->undeleted = 1;
4843 /* if box = 0, check for new, if box = 1, check for read */
4844 if (box == 0) {
4845 pgm->unseen = 1;
4846 pgm->seen = 0;
4847 } else if (box == 1) {
4848 pgm->seen = 1;
4849 pgm->unseen = 0;
4852 vms->vmArrayIndex = 0;
4853 if (option_debug > 2)
4854 ast_log(LOG_DEBUG,"Before mail_search_full, user is %s\n",vmu->imapuser);
4855 mail_search_full (vms->mailstream, NULL, pgm, NIL);
4858 vms->lastmsg = vms->vmArrayIndex - 1;
4860 mail_free_searchpgm(&pgm);
4861 return 0;
4863 #else
4864 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
4866 int res = 0;
4867 int count_msg, last_msg;
4869 ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
4871 /* Rename the member vmbox HERE so that we don't try to return before
4872 * we know what's going on.
4874 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
4876 /* Faster to make the directory than to check if it exists. */
4877 create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
4879 count_msg = count_messages(vmu, vms->curdir);
4880 if (count_msg < 0)
4881 return count_msg;
4882 else
4883 vms->lastmsg = count_msg - 1;
4886 The following test is needed in case sequencing gets messed up.
4887 There appears to be more than one way to mess up sequence, so
4888 we will not try to find all of the root causes--just fix it when
4889 detected.
4892 last_msg = last_message_index(vmu, vms->curdir);
4893 if (last_msg < 0)
4894 return last_msg;
4895 else if (vms->lastmsg != last_msg)
4897 ast_log(LOG_NOTICE, "Resequencing Mailbox: %s\n", vms->curdir);
4898 res = resequence_mailbox(vmu, vms->curdir);
4899 if (res)
4900 return res;
4903 return 0;
4905 #endif
4907 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
4909 int x = 0;
4910 #ifndef IMAP_STORAGE
4911 int res = 0, nummsg;
4912 #endif
4914 if (vms->lastmsg <= -1)
4915 goto done;
4917 vms->curmsg = -1;
4918 #ifndef IMAP_STORAGE
4919 /* Get the deleted messages fixed */
4920 if (vm_lock_path(vms->curdir))
4921 return ERROR_LOCK_PATH;
4923 for (x = 0; x < vmu->maxmsg; x++) {
4924 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
4925 /* Save this message. It's not in INBOX or hasn't been heard */
4926 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
4927 if (!EXISTS(vms->curdir, x, vms->fn, NULL))
4928 break;
4929 vms->curmsg++;
4930 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
4931 if (strcmp(vms->fn, vms->fn2)) {
4932 RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
4934 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
4935 /* Move to old folder before deleting */
4936 res = save_to_folder(vmu, vms, x, 1);
4937 if (res == ERROR_LOCK_PATH || res == ERROR_MAILBOX_FULL) {
4938 /* If save failed do not delete the message */
4939 ast_log(LOG_WARNING, "Save failed. Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
4940 vms->deleted[x] = 0;
4941 vms->heard[x] = 0;
4942 --x;
4947 /* Delete ALL remaining messages */
4948 nummsg = x - 1;
4949 for (x = vms->curmsg + 1; x <= nummsg; x++) {
4950 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
4951 if (EXISTS(vms->curdir, x, vms->fn, NULL))
4952 DELETE(vms->curdir, x, vms->fn);
4954 ast_unlock_path(vms->curdir);
4955 #else
4956 if (vms->deleted) {
4957 for (x=0;x < vmu->maxmsg;x++) {
4958 if (vms->deleted[x]) {
4959 if (option_debug > 2)
4960 ast_log(LOG_DEBUG,"IMAP delete of %d\n",x);
4961 IMAP_DELETE(vms->curdir, x, vms->fn, vms);
4965 #endif
4967 done:
4968 if (vms->deleted)
4969 memset(vms->deleted, 0, vmu->maxmsg * sizeof(int));
4970 if (vms->heard)
4971 memset(vms->heard, 0, vmu->maxmsg * sizeof(int));
4973 return 0;
4976 /* In Greek even though we CAN use a syntax like "friends messages"
4977 * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
4978 * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
4979 * syntax for the above three categories which is more elegant.
4982 static int vm_play_folder_name_gr(struct ast_channel *chan, char *mbox)
4984 int cmd;
4985 char *buf;
4987 buf = alloca(strlen(mbox)+2);
4988 strcpy(buf, mbox);
4989 strcat(buf,"s");
4991 if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")){
4992 cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
4993 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
4994 } else {
4995 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
4996 return cmd ? cmd : ast_play_and_wait(chan, mbox); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
5000 static int vm_play_folder_name_pl(struct ast_channel *chan, char *mbox)
5002 int cmd;
5004 if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")) {
5005 if (!strcasecmp(mbox, "vm-INBOX"))
5006 cmd = ast_play_and_wait(chan, "vm-new-e");
5007 else
5008 cmd = ast_play_and_wait(chan, "vm-old-e");
5009 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
5010 } else {
5011 cmd = ast_play_and_wait(chan, "vm-messages");
5012 return cmd ? cmd : ast_play_and_wait(chan, mbox);
5016 static int vm_play_folder_name_ua(struct ast_channel *chan, char *mbox)
5018 int cmd;
5020 if (!strcasecmp(mbox, "vm-Family") || !strcasecmp(mbox, "vm-Friends") || !strcasecmp(mbox, "vm-Work")){
5021 cmd = ast_play_and_wait(chan, "vm-messages");
5022 return cmd ? cmd : ast_play_and_wait(chan, mbox);
5023 } else {
5024 cmd = ast_play_and_wait(chan, mbox);
5025 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
5029 static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
5031 int cmd;
5033 if (!strcasecmp(chan->language, "it") || !strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* Italian, Spanish, French or Portuguese syntax */
5034 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
5035 return cmd ? cmd : ast_play_and_wait(chan, mbox);
5036 } else if (!strcasecmp(chan->language, "gr")){
5037 return vm_play_folder_name_gr(chan, mbox);
5038 } else if (!strcasecmp(chan->language, "pl")){
5039 return vm_play_folder_name_pl(chan, mbox);
5040 } else if (!strcasecmp(chan->language, "ua")){ /* Ukrainian syntax */
5041 return vm_play_folder_name_ua(chan, mbox);
5042 } else { /* Default English */
5043 cmd = ast_play_and_wait(chan, mbox);
5044 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
5048 /* GREEK SYNTAX
5049 In greek the plural for old/new is
5050 different so we need the following files
5051 We also need vm-denExeteMynhmata because
5052 this syntax is different.
5054 -> vm-Olds.wav : "Palia"
5055 -> vm-INBOXs.wav : "Nea"
5056 -> vm-denExeteMynhmata : "den exete mynhmata"
5060 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
5062 int res = 0;
5064 if (vms->newmessages) {
5065 res = ast_play_and_wait(chan, "vm-youhave");
5066 if (!res)
5067 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
5068 if (!res) {
5069 if ((vms->newmessages == 1)) {
5070 res = ast_play_and_wait(chan, "vm-INBOX");
5071 if (!res)
5072 res = ast_play_and_wait(chan, "vm-message");
5073 } else {
5074 res = ast_play_and_wait(chan, "vm-INBOXs");
5075 if (!res)
5076 res = ast_play_and_wait(chan, "vm-messages");
5079 } else if (vms->oldmessages){
5080 res = ast_play_and_wait(chan, "vm-youhave");
5081 if (!res)
5082 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
5083 if ((vms->oldmessages == 1)){
5084 res = ast_play_and_wait(chan, "vm-Old");
5085 if (!res)
5086 res = ast_play_and_wait(chan, "vm-message");
5087 } else {
5088 res = ast_play_and_wait(chan, "vm-Olds");
5089 if (!res)
5090 res = ast_play_and_wait(chan, "vm-messages");
5092 } else if (!vms->oldmessages && !vms->newmessages)
5093 res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
5094 return res;
5097 /* Default English syntax */
5098 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
5100 int res;
5102 /* Introduce messages they have */
5103 res = ast_play_and_wait(chan, "vm-youhave");
5104 if (!res) {
5105 if (vms->newmessages) {
5106 res = say_and_wait(chan, vms->newmessages, chan->language);
5107 if (!res)
5108 res = ast_play_and_wait(chan, "vm-INBOX");
5109 if (vms->oldmessages && !res)
5110 res = ast_play_and_wait(chan, "vm-and");
5111 else if (!res) {
5112 if ((vms->newmessages == 1))
5113 res = ast_play_and_wait(chan, "vm-message");
5114 else
5115 res = ast_play_and_wait(chan, "vm-messages");
5119 if (!res && vms->oldmessages) {
5120 res = say_and_wait(chan, vms->oldmessages, chan->language);
5121 if (!res)
5122 res = ast_play_and_wait(chan, "vm-Old");
5123 if (!res) {
5124 if (vms->oldmessages == 1)
5125 res = ast_play_and_wait(chan, "vm-message");
5126 else
5127 res = ast_play_and_wait(chan, "vm-messages");
5130 if (!res) {
5131 if (!vms->oldmessages && !vms->newmessages) {
5132 res = ast_play_and_wait(chan, "vm-no");
5133 if (!res)
5134 res = ast_play_and_wait(chan, "vm-messages");
5138 return res;
5141 /* ITALIAN syntax */
5142 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
5144 /* Introduce messages they have */
5145 int res;
5146 if (!vms->oldmessages && !vms->newmessages)
5147 res = ast_play_and_wait(chan, "vm-no") ||
5148 ast_play_and_wait(chan, "vm-message");
5149 else
5150 res = ast_play_and_wait(chan, "vm-youhave");
5151 if (!res && vms->newmessages) {
5152 res = (vms->newmessages == 1) ?
5153 ast_play_and_wait(chan, "digits/un") ||
5154 ast_play_and_wait(chan, "vm-nuovo") ||
5155 ast_play_and_wait(chan, "vm-message") :
5156 /* 2 or more new messages */
5157 say_and_wait(chan, vms->newmessages, chan->language) ||
5158 ast_play_and_wait(chan, "vm-nuovi") ||
5159 ast_play_and_wait(chan, "vm-messages");
5160 if (!res && vms->oldmessages)
5161 res = ast_play_and_wait(chan, "vm-and");
5163 if (!res && vms->oldmessages) {
5164 res = (vms->oldmessages == 1) ?
5165 ast_play_and_wait(chan, "digits/un") ||
5166 ast_play_and_wait(chan, "vm-vecchio") ||
5167 ast_play_and_wait(chan, "vm-message") :
5168 /* 2 or more old messages */
5169 say_and_wait(chan, vms->oldmessages, chan->language) ||
5170 ast_play_and_wait(chan, "vm-vecchi") ||
5171 ast_play_and_wait(chan, "vm-messages");
5173 return res ? -1 : 0;
5176 /* POLISH syntax */
5177 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
5179 /* Introduce messages they have */
5180 int res;
5181 div_t num;
5183 if (!vms->oldmessages && !vms->newmessages) {
5184 res = ast_play_and_wait(chan, "vm-no");
5185 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5186 return res;
5187 } else {
5188 res = ast_play_and_wait(chan, "vm-youhave");
5191 if (vms->newmessages) {
5192 num = div(vms->newmessages, 10);
5193 if (vms->newmessages == 1) {
5194 res = ast_play_and_wait(chan, "digits/1-a");
5195 res = res ? res : ast_play_and_wait(chan, "vm-new-a");
5196 res = res ? res : ast_play_and_wait(chan, "vm-message");
5197 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
5198 if (num.rem == 2) {
5199 if (!num.quot) {
5200 res = ast_play_and_wait(chan, "digits/2-ie");
5201 } else {
5202 res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
5203 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
5205 } else {
5206 res = say_and_wait(chan, vms->newmessages, chan->language);
5208 res = res ? res : ast_play_and_wait(chan, "vm-new-e");
5209 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5210 } else {
5211 res = say_and_wait(chan, vms->newmessages, chan->language);
5212 res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
5213 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5215 if (!res && vms->oldmessages)
5216 res = ast_play_and_wait(chan, "vm-and");
5218 if (!res && vms->oldmessages) {
5219 num = div(vms->oldmessages, 10);
5220 if (vms->oldmessages == 1) {
5221 res = ast_play_and_wait(chan, "digits/1-a");
5222 res = res ? res : ast_play_and_wait(chan, "vm-old-a");
5223 res = res ? res : ast_play_and_wait(chan, "vm-message");
5224 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
5225 if (num.rem == 2) {
5226 if (!num.quot) {
5227 res = ast_play_and_wait(chan, "digits/2-ie");
5228 } else {
5229 res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
5230 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
5232 } else {
5233 res = say_and_wait(chan, vms->oldmessages, chan->language);
5235 res = res ? res : ast_play_and_wait(chan, "vm-old-e");
5236 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5237 } else {
5238 res = say_and_wait(chan, vms->oldmessages, chan->language);
5239 res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
5240 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5244 return res;
5247 /* SWEDISH syntax */
5248 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
5250 /* Introduce messages they have */
5251 int res;
5253 res = ast_play_and_wait(chan, "vm-youhave");
5254 if (res)
5255 return res;
5257 if (!vms->oldmessages && !vms->newmessages) {
5258 res = ast_play_and_wait(chan, "vm-no");
5259 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5260 return res;
5263 if (vms->newmessages) {
5264 if ((vms->newmessages == 1)) {
5265 res = ast_play_and_wait(chan, "digits/ett");
5266 res = res ? res : ast_play_and_wait(chan, "vm-nytt");
5267 res = res ? res : ast_play_and_wait(chan, "vm-message");
5268 } else {
5269 res = say_and_wait(chan, vms->newmessages, chan->language);
5270 res = res ? res : ast_play_and_wait(chan, "vm-nya");
5271 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5273 if (!res && vms->oldmessages)
5274 res = ast_play_and_wait(chan, "vm-and");
5276 if (!res && vms->oldmessages) {
5277 if (vms->oldmessages == 1) {
5278 res = ast_play_and_wait(chan, "digits/ett");
5279 res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
5280 res = res ? res : ast_play_and_wait(chan, "vm-message");
5281 } else {
5282 res = say_and_wait(chan, vms->oldmessages, chan->language);
5283 res = res ? res : ast_play_and_wait(chan, "vm-gamla");
5284 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5288 return res;
5291 /* NORWEGIAN syntax */
5292 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
5294 /* Introduce messages they have */
5295 int res;
5297 res = ast_play_and_wait(chan, "vm-youhave");
5298 if (res)
5299 return res;
5301 if (!vms->oldmessages && !vms->newmessages) {
5302 res = ast_play_and_wait(chan, "vm-no");
5303 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5304 return res;
5307 if (vms->newmessages) {
5308 if ((vms->newmessages == 1)) {
5309 res = ast_play_and_wait(chan, "digits/1");
5310 res = res ? res : ast_play_and_wait(chan, "vm-ny");
5311 res = res ? res : ast_play_and_wait(chan, "vm-message");
5312 } else {
5313 res = say_and_wait(chan, vms->newmessages, chan->language);
5314 res = res ? res : ast_play_and_wait(chan, "vm-nye");
5315 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5317 if (!res && vms->oldmessages)
5318 res = ast_play_and_wait(chan, "vm-and");
5320 if (!res && vms->oldmessages) {
5321 if (vms->oldmessages == 1) {
5322 res = ast_play_and_wait(chan, "digits/1");
5323 res = res ? res : ast_play_and_wait(chan, "vm-gamel");
5324 res = res ? res : ast_play_and_wait(chan, "vm-message");
5325 } else {
5326 res = say_and_wait(chan, vms->oldmessages, chan->language);
5327 res = res ? res : ast_play_and_wait(chan, "vm-gamle");
5328 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5332 return res;
5335 /* GERMAN syntax */
5336 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
5338 /* Introduce messages they have */
5339 int res;
5340 res = ast_play_and_wait(chan, "vm-youhave");
5341 if (!res) {
5342 if (vms->newmessages) {
5343 if ((vms->newmessages == 1))
5344 res = ast_play_and_wait(chan, "digits/1F");
5345 else
5346 res = say_and_wait(chan, vms->newmessages, chan->language);
5347 if (!res)
5348 res = ast_play_and_wait(chan, "vm-INBOX");
5349 if (vms->oldmessages && !res)
5350 res = ast_play_and_wait(chan, "vm-and");
5351 else if (!res) {
5352 if ((vms->newmessages == 1))
5353 res = ast_play_and_wait(chan, "vm-message");
5354 else
5355 res = ast_play_and_wait(chan, "vm-messages");
5359 if (!res && vms->oldmessages) {
5360 if (vms->oldmessages == 1)
5361 res = ast_play_and_wait(chan, "digits/1F");
5362 else
5363 res = say_and_wait(chan, vms->oldmessages, chan->language);
5364 if (!res)
5365 res = ast_play_and_wait(chan, "vm-Old");
5366 if (!res) {
5367 if (vms->oldmessages == 1)
5368 res = ast_play_and_wait(chan, "vm-message");
5369 else
5370 res = ast_play_and_wait(chan, "vm-messages");
5373 if (!res) {
5374 if (!vms->oldmessages && !vms->newmessages) {
5375 res = ast_play_and_wait(chan, "vm-no");
5376 if (!res)
5377 res = ast_play_and_wait(chan, "vm-messages");
5381 return res;
5384 /* SPANISH syntax */
5385 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
5387 /* Introduce messages they have */
5388 int res;
5389 if (!vms->oldmessages && !vms->newmessages) {
5390 res = ast_play_and_wait(chan, "vm-youhaveno");
5391 if (!res)
5392 res = ast_play_and_wait(chan, "vm-messages");
5393 } else {
5394 res = ast_play_and_wait(chan, "vm-youhave");
5396 if (!res) {
5397 if (vms->newmessages) {
5398 if (!res) {
5399 if ((vms->newmessages == 1)) {
5400 res = ast_play_and_wait(chan, "digits/1M");
5401 if (!res)
5402 res = ast_play_and_wait(chan, "vm-message");
5403 if (!res)
5404 res = ast_play_and_wait(chan, "vm-INBOXs");
5405 } else {
5406 res = say_and_wait(chan, vms->newmessages, chan->language);
5407 if (!res)
5408 res = ast_play_and_wait(chan, "vm-messages");
5409 if (!res)
5410 res = ast_play_and_wait(chan, "vm-INBOX");
5413 if (vms->oldmessages && !res)
5414 res = ast_play_and_wait(chan, "vm-and");
5416 if (vms->oldmessages) {
5417 if (!res) {
5418 if (vms->oldmessages == 1) {
5419 res = ast_play_and_wait(chan, "digits/1M");
5420 if (!res)
5421 res = ast_play_and_wait(chan, "vm-message");
5422 if (!res)
5423 res = ast_play_and_wait(chan, "vm-Olds");
5424 } else {
5425 res = say_and_wait(chan, vms->oldmessages, chan->language);
5426 if (!res)
5427 res = ast_play_and_wait(chan, "vm-messages");
5428 if (!res)
5429 res = ast_play_and_wait(chan, "vm-Old");
5434 return res;
5437 /* BRAZILIAN PORTUGUESE syntax */
5438 static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
5439 /* Introduce messages they have */
5440 int res;
5441 if (!vms->oldmessages && !vms->newmessages) {
5442 res = ast_play_and_wait(chan, "vm-nomessages");
5443 return res;
5445 else {
5446 res = ast_play_and_wait(chan, "vm-youhave");
5448 if (vms->newmessages) {
5449 if (!res)
5450 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
5451 if ((vms->newmessages == 1)) {
5452 if (!res)
5453 res = ast_play_and_wait(chan, "vm-message");
5454 if (!res)
5455 res = ast_play_and_wait(chan, "vm-INBOXs");
5457 else {
5458 if (!res)
5459 res = ast_play_and_wait(chan, "vm-messages");
5460 if (!res)
5461 res = ast_play_and_wait(chan, "vm-INBOX");
5463 if (vms->oldmessages && !res)
5464 res = ast_play_and_wait(chan, "vm-and");
5466 if (vms->oldmessages) {
5467 if (!res)
5468 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
5469 if (vms->oldmessages == 1) {
5470 if (!res)
5471 res = ast_play_and_wait(chan, "vm-message");
5472 if (!res)
5473 res = ast_play_and_wait(chan, "vm-Olds");
5475 else {
5476 if (!res)
5477 res = ast_play_and_wait(chan, "vm-messages");
5478 if (!res)
5479 res = ast_play_and_wait(chan, "vm-Old");
5482 return res;
5485 /* FRENCH syntax */
5486 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
5488 /* Introduce messages they have */
5489 int res;
5490 res = ast_play_and_wait(chan, "vm-youhave");
5491 if (!res) {
5492 if (vms->newmessages) {
5493 res = say_and_wait(chan, vms->newmessages, chan->language);
5494 if (!res)
5495 res = ast_play_and_wait(chan, "vm-INBOX");
5496 if (vms->oldmessages && !res)
5497 res = ast_play_and_wait(chan, "vm-and");
5498 else if (!res) {
5499 if ((vms->newmessages == 1))
5500 res = ast_play_and_wait(chan, "vm-message");
5501 else
5502 res = ast_play_and_wait(chan, "vm-messages");
5506 if (!res && vms->oldmessages) {
5507 res = say_and_wait(chan, vms->oldmessages, chan->language);
5508 if (!res)
5509 res = ast_play_and_wait(chan, "vm-Old");
5510 if (!res) {
5511 if (vms->oldmessages == 1)
5512 res = ast_play_and_wait(chan, "vm-message");
5513 else
5514 res = ast_play_and_wait(chan, "vm-messages");
5517 if (!res) {
5518 if (!vms->oldmessages && !vms->newmessages) {
5519 res = ast_play_and_wait(chan, "vm-no");
5520 if (!res)
5521 res = ast_play_and_wait(chan, "vm-messages");
5525 return res;
5528 /* DUTCH syntax */
5529 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
5531 /* Introduce messages they have */
5532 int res;
5533 res = ast_play_and_wait(chan, "vm-youhave");
5534 if (!res) {
5535 if (vms->newmessages) {
5536 res = say_and_wait(chan, vms->newmessages, chan->language);
5537 if (!res) {
5538 if (vms->newmessages == 1)
5539 res = ast_play_and_wait(chan, "vm-INBOXs");
5540 else
5541 res = ast_play_and_wait(chan, "vm-INBOX");
5543 if (vms->oldmessages && !res)
5544 res = ast_play_and_wait(chan, "vm-and");
5545 else if (!res) {
5546 if ((vms->newmessages == 1))
5547 res = ast_play_and_wait(chan, "vm-message");
5548 else
5549 res = ast_play_and_wait(chan, "vm-messages");
5553 if (!res && vms->oldmessages) {
5554 res = say_and_wait(chan, vms->oldmessages, chan->language);
5555 if (!res) {
5556 if (vms->oldmessages == 1)
5557 res = ast_play_and_wait(chan, "vm-Olds");
5558 else
5559 res = ast_play_and_wait(chan, "vm-Old");
5561 if (!res) {
5562 if (vms->oldmessages == 1)
5563 res = ast_play_and_wait(chan, "vm-message");
5564 else
5565 res = ast_play_and_wait(chan, "vm-messages");
5568 if (!res) {
5569 if (!vms->oldmessages && !vms->newmessages) {
5570 res = ast_play_and_wait(chan, "vm-no");
5571 if (!res)
5572 res = ast_play_and_wait(chan, "vm-messages");
5576 return res;
5579 /* PORTUGUESE syntax */
5580 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
5582 /* Introduce messages they have */
5583 int res;
5584 res = ast_play_and_wait(chan, "vm-youhave");
5585 if (!res) {
5586 if (vms->newmessages) {
5587 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
5588 if (!res) {
5589 if ((vms->newmessages == 1)) {
5590 res = ast_play_and_wait(chan, "vm-message");
5591 if (!res)
5592 res = ast_play_and_wait(chan, "vm-INBOXs");
5593 } else {
5594 res = ast_play_and_wait(chan, "vm-messages");
5595 if (!res)
5596 res = ast_play_and_wait(chan, "vm-INBOX");
5599 if (vms->oldmessages && !res)
5600 res = ast_play_and_wait(chan, "vm-and");
5602 if (!res && vms->oldmessages) {
5603 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
5604 if (!res) {
5605 if (vms->oldmessages == 1) {
5606 res = ast_play_and_wait(chan, "vm-message");
5607 if (!res)
5608 res = ast_play_and_wait(chan, "vm-Olds");
5609 } else {
5610 res = ast_play_and_wait(chan, "vm-messages");
5611 if (!res)
5612 res = ast_play_and_wait(chan, "vm-Old");
5616 if (!res) {
5617 if (!vms->oldmessages && !vms->newmessages) {
5618 res = ast_play_and_wait(chan, "vm-no");
5619 if (!res)
5620 res = ast_play_and_wait(chan, "vm-messages");
5624 return res;
5628 /* CZECH syntax */
5629 /* in czech there must be declension of word new and message
5630 * czech : english : czech : english
5631 * --------------------------------------------------------
5632 * vm-youhave : you have
5633 * vm-novou : one new : vm-zpravu : message
5634 * vm-nove : 2-4 new : vm-zpravy : messages
5635 * vm-novych : 5-infinite new : vm-zprav : messages
5636 * vm-starou : one old
5637 * vm-stare : 2-4 old
5638 * vm-starych : 5-infinite old
5639 * jednu : one - falling 4.
5640 * vm-no : no ( no messages )
5643 static int vm_intro_cz(struct ast_channel *chan,struct vm_state *vms)
5645 int res;
5646 res = ast_play_and_wait(chan, "vm-youhave");
5647 if (!res) {
5648 if (vms->newmessages) {
5649 if (vms->newmessages == 1) {
5650 res = ast_play_and_wait(chan, "digits/jednu");
5651 } else {
5652 res = say_and_wait(chan, vms->newmessages, chan->language);
5654 if (!res) {
5655 if ((vms->newmessages == 1))
5656 res = ast_play_and_wait(chan, "vm-novou");
5657 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
5658 res = ast_play_and_wait(chan, "vm-nove");
5659 if (vms->newmessages > 4)
5660 res = ast_play_and_wait(chan, "vm-novych");
5662 if (vms->oldmessages && !res)
5663 res = ast_play_and_wait(chan, "vm-and");
5664 else if (!res) {
5665 if ((vms->newmessages == 1))
5666 res = ast_play_and_wait(chan, "vm-zpravu");
5667 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
5668 res = ast_play_and_wait(chan, "vm-zpravy");
5669 if (vms->newmessages > 4)
5670 res = ast_play_and_wait(chan, "vm-zprav");
5673 if (!res && vms->oldmessages) {
5674 res = say_and_wait(chan, vms->oldmessages, chan->language);
5675 if (!res) {
5676 if ((vms->oldmessages == 1))
5677 res = ast_play_and_wait(chan, "vm-starou");
5678 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
5679 res = ast_play_and_wait(chan, "vm-stare");
5680 if (vms->oldmessages > 4)
5681 res = ast_play_and_wait(chan, "vm-starych");
5683 if (!res) {
5684 if ((vms->oldmessages == 1))
5685 res = ast_play_and_wait(chan, "vm-zpravu");
5686 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
5687 res = ast_play_and_wait(chan, "vm-zpravy");
5688 if (vms->oldmessages > 4)
5689 res = ast_play_and_wait(chan, "vm-zprav");
5692 if (!res) {
5693 if (!vms->oldmessages && !vms->newmessages) {
5694 res = ast_play_and_wait(chan, "vm-no");
5695 if (!res)
5696 res = ast_play_and_wait(chan, "vm-zpravy");
5700 return res;
5703 static int get_lastdigits(int num)
5705 num %= 100;
5706 return (num < 20) ? num : num % 10;
5709 static int vm_intro_ru(struct ast_channel *chan,struct vm_state *vms)
5711 int res;
5712 int lastnum = 0;
5713 int dcnum;
5715 res = ast_play_and_wait(chan, "vm-youhave");
5716 if (!res && vms->newmessages) {
5717 lastnum = get_lastdigits(vms->newmessages);
5718 dcnum = vms->newmessages - lastnum;
5719 if (dcnum)
5720 res = say_and_wait(chan, dcnum, chan->language);
5721 if (!res && lastnum) {
5722 if (lastnum == 1)
5723 res = ast_play_and_wait(chan, "digits/odno");
5724 else
5725 res = say_and_wait(chan, lastnum, chan->language);
5728 if (!res)
5729 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-novoe" : "vm-novyh");
5731 if (!res && vms->oldmessages)
5732 res = ast_play_and_wait(chan, "vm-and");
5735 if (!res && vms->oldmessages) {
5736 lastnum = get_lastdigits(vms->oldmessages);
5737 dcnum = vms->oldmessages - lastnum;
5738 if (dcnum)
5739 res = say_and_wait(chan, dcnum, chan->language);
5740 if (!res && lastnum) {
5741 if (lastnum == 1)
5742 res = ast_play_and_wait(chan, "digits/odno");
5743 else
5744 res = say_and_wait(chan, lastnum, chan->language);
5747 if (!res)
5748 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-staroe" : "vm-staryh");
5751 if (!res && !vms->newmessages && !vms->oldmessages) {
5752 lastnum = 0;
5753 res = ast_play_and_wait(chan, "vm-no");
5756 if (!res) {
5757 switch (lastnum) {
5758 case 1:
5759 res = ast_play_and_wait(chan, "vm-soobshenie");
5760 break;
5761 case 2:
5762 case 3:
5763 case 4:
5764 res = ast_play_and_wait(chan, "vm-soobsheniya");
5765 break;
5766 default:
5767 res = ast_play_and_wait(chan, "vm-soobsheniy");
5768 break;
5772 return res;
5775 /* UKRAINIAN syntax */
5776 /* in ukrainian the syntax is different so we need the following files
5777 * --------------------------------------------------------
5778 * /digits/ua/1e 'odne'
5779 * vm-nove 'nove'
5780 * vm-stare 'stare'
5783 static int vm_intro_ua(struct ast_channel *chan,struct vm_state *vms)
5785 int res;
5786 int lastnum = 0;
5787 int dcnum;
5789 res = ast_play_and_wait(chan, "vm-youhave");
5790 if (!res && vms->newmessages) {
5791 lastnum = get_lastdigits(vms->newmessages);
5792 dcnum = vms->newmessages - lastnum;
5793 if (dcnum)
5794 res = say_and_wait(chan, dcnum, chan->language);
5795 if (!res && lastnum) {
5796 if (lastnum == 1)
5797 res = ast_play_and_wait(chan, "digits/ua/1e");
5798 else
5799 res = say_and_wait(chan, lastnum, chan->language);
5802 if (!res)
5803 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-nove" : "vm-INBOX");
5805 if (!res && vms->oldmessages)
5806 res = ast_play_and_wait(chan, "vm-and");
5809 if (!res && vms->oldmessages) {
5810 lastnum = get_lastdigits(vms->oldmessages);
5811 dcnum = vms->oldmessages - lastnum;
5812 if (dcnum)
5813 res = say_and_wait(chan, dcnum, chan->language);
5814 if (!res && lastnum) {
5815 if (lastnum == 1)
5816 res = ast_play_and_wait(chan, "digits/ua/1e");
5817 else
5818 res = say_and_wait(chan, lastnum, chan->language);
5821 if (!res)
5822 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-stare" : "vm-Old");
5825 if (!res && !vms->newmessages && !vms->oldmessages) {
5826 lastnum = 0;
5827 res = ast_play_and_wait(chan, "vm-no");
5830 if (!res) {
5831 switch (lastnum) {
5832 case 1:
5833 case 2:
5834 case 3:
5835 case 4:
5836 res = ast_play_and_wait(chan, "vm-message");
5837 break;
5838 default:
5839 res = ast_play_and_wait(chan, "vm-messages");
5840 break;
5844 return res;
5848 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
5850 char prefile[256];
5852 /* Notify the user that the temp greeting is set and give them the option to remove it */
5853 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
5854 if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
5855 if (ast_fileexists(prefile, NULL, NULL) > 0)
5856 ast_play_and_wait(chan, "vm-tempgreetactive");
5859 /* Play voicemail intro - syntax is different for different languages */
5860 if (!strcasecmp(chan->language, "de")) { /* GERMAN syntax */
5861 return vm_intro_de(chan, vms);
5862 } else if (!strcasecmp(chan->language, "es")) { /* SPANISH syntax */
5863 return vm_intro_es(chan, vms);
5864 } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN syntax */
5865 return vm_intro_it(chan, vms);
5866 } else if (!strcasecmp(chan->language, "fr")) { /* FRENCH syntax */
5867 return vm_intro_fr(chan, vms);
5868 } else if (!strcasecmp(chan->language, "nl")) { /* DUTCH syntax */
5869 return vm_intro_nl(chan, vms);
5870 } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE syntax */
5871 return vm_intro_pt(chan, vms);
5872 } else if (!strcasecmp(chan->language, "pt_BR")) { /* BRAZILIAN PORTUGUESE syntax */
5873 return vm_intro_pt_BR(chan, vms);
5874 } else if (!strcasecmp(chan->language, "cz")) { /* CZECH syntax */
5875 return vm_intro_cz(chan, vms);
5876 } else if (!strcasecmp(chan->language, "gr")) { /* GREEK syntax */
5877 return vm_intro_gr(chan, vms);
5878 } else if (!strcasecmp(chan->language, "pl")) { /* POLISH syntax */
5879 return vm_intro_pl(chan, vms);
5880 } else if (!strcasecmp(chan->language, "se")) { /* SWEDISH syntax */
5881 return vm_intro_se(chan, vms);
5882 } else if (!strcasecmp(chan->language, "no")) { /* NORWEGIAN syntax */
5883 return vm_intro_no(chan, vms);
5884 } else if (!strcasecmp(chan->language, "ru")) { /* RUSSIAN syntax */
5885 return vm_intro_ru(chan, vms);
5886 } else if (!strcasecmp(chan->language, "ua")) { /* UKRAINIAN syntax */
5887 return vm_intro_ua(chan, vms);
5888 } else { /* Default to ENGLISH */
5889 return vm_intro_en(chan, vms);
5893 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
5895 int res = 0;
5896 /* Play instructions and wait for new command */
5897 while (!res) {
5898 if (vms->starting) {
5899 if (vms->lastmsg > -1) {
5900 res = ast_play_and_wait(chan, "vm-onefor");
5901 if (!res)
5902 res = vm_play_folder_name(chan, vms->vmbox);
5904 if (!res)
5905 res = ast_play_and_wait(chan, "vm-opts");
5906 } else {
5907 if (vms->curmsg)
5908 res = ast_play_and_wait(chan, "vm-prev");
5909 if (!res && !skipadvanced)
5910 res = ast_play_and_wait(chan, "vm-advopts");
5911 if (!res)
5912 res = ast_play_and_wait(chan, "vm-repeat");
5913 if (!res && (vms->curmsg != vms->lastmsg))
5914 res = ast_play_and_wait(chan, "vm-next");
5915 if (!res) {
5916 if (!vms->deleted[vms->curmsg])
5917 res = ast_play_and_wait(chan, "vm-delete");
5918 else
5919 res = ast_play_and_wait(chan, "vm-undelete");
5920 if (!res)
5921 res = ast_play_and_wait(chan, "vm-toforward");
5922 if (!res)
5923 res = ast_play_and_wait(chan, "vm-savemessage");
5926 if (!res)
5927 res = ast_play_and_wait(chan, "vm-helpexit");
5928 if (!res)
5929 res = ast_waitfordigit(chan, 6000);
5930 if (!res) {
5931 vms->repeats++;
5932 if (vms->repeats > 2) {
5933 res = 't';
5937 return res;
5940 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
5942 int cmd = 0;
5943 int duration = 0;
5944 int tries = 0;
5945 char newpassword[80] = "";
5946 char newpassword2[80] = "";
5947 char prefile[PATH_MAX] = "";
5948 unsigned char buf[256];
5949 int bytes=0;
5951 if (ast_adsi_available(chan)) {
5952 bytes += adsi_logo(buf + bytes);
5953 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
5954 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
5955 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5956 bytes += ast_adsi_voice_mode(buf + bytes, 0);
5957 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5960 /* First, have the user change their password
5961 so they won't get here again */
5962 for (;;) {
5963 newpassword[1] = '\0';
5964 newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
5965 if (cmd == '#')
5966 newpassword[0] = '\0';
5967 if (cmd < 0 || cmd == 't' || cmd == '#')
5968 return cmd;
5969 cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
5970 if (cmd < 0 || cmd == 't' || cmd == '#')
5971 return cmd;
5972 newpassword2[1] = '\0';
5973 newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
5974 if (cmd == '#')
5975 newpassword2[0] = '\0';
5976 if (cmd < 0 || cmd == 't' || cmd == '#')
5977 return cmd;
5978 cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#");
5979 if (cmd < 0 || cmd == 't' || cmd == '#')
5980 return cmd;
5981 if (!strcmp(newpassword, newpassword2))
5982 break;
5983 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
5984 cmd = ast_play_and_wait(chan, "vm-mismatch");
5985 if (++tries == 3)
5986 return -1;
5988 if (ast_strlen_zero(ext_pass_cmd))
5989 vm_change_password(vmu,newpassword);
5990 else
5991 vm_change_password_shell(vmu,newpassword);
5992 if (option_debug > 2)
5993 ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
5994 cmd = ast_play_and_wait(chan,"vm-passchanged");
5996 /* If forcename is set, have the user record their name */
5997 if (ast_test_flag(vmu, VM_FORCENAME)) {
5998 snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
5999 if (ast_fileexists(prefile, NULL, NULL) < 1) {
6000 #ifndef IMAP_STORAGE
6001 cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6002 #else
6003 cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6004 #endif
6005 if (cmd < 0 || cmd == 't' || cmd == '#')
6006 return cmd;
6010 /* If forcegreetings is set, have the user record their greetings */
6011 if (ast_test_flag(vmu, VM_FORCEGREET)) {
6012 snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
6013 if (ast_fileexists(prefile, NULL, NULL) < 1) {
6014 #ifndef IMAP_STORAGE
6015 cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6016 #else
6017 cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6018 #endif
6019 if (cmd < 0 || cmd == 't' || cmd == '#')
6020 return cmd;
6023 snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
6024 if (ast_fileexists(prefile, NULL, NULL) < 1) {
6025 #ifndef IMAP_STORAGE
6026 cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6027 #else
6028 cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6029 #endif
6030 if (cmd < 0 || cmd == 't' || cmd == '#')
6031 return cmd;
6035 return cmd;
6038 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
6040 int cmd = 0;
6041 int retries = 0;
6042 int duration = 0;
6043 char newpassword[80] = "";
6044 char newpassword2[80] = "";
6045 char prefile[PATH_MAX] = "";
6046 unsigned char buf[256];
6047 int bytes=0;
6049 if (ast_adsi_available(chan))
6051 bytes += adsi_logo(buf + bytes);
6052 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
6053 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
6054 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6055 bytes += ast_adsi_voice_mode(buf + bytes, 0);
6056 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6058 while ((cmd >= 0) && (cmd != 't')) {
6059 if (cmd)
6060 retries = 0;
6061 switch (cmd) {
6062 case '1':
6063 snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
6064 #ifndef IMAP_STORAGE
6065 cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6066 #else
6067 cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6068 #endif
6069 break;
6070 case '2':
6071 snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
6072 #ifndef IMAP_STORAGE
6073 cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6074 #else
6075 cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6076 #endif
6077 break;
6078 case '3':
6079 snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
6080 #ifndef IMAP_STORAGE
6081 cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6082 #else
6083 cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6084 #endif
6085 break;
6086 case '4':
6087 cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
6088 break;
6089 case '5':
6090 if (vmu->password[0] == '-') {
6091 cmd = ast_play_and_wait(chan, "vm-no");
6092 break;
6094 newpassword[1] = '\0';
6095 newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
6096 if (cmd == '#')
6097 newpassword[0] = '\0';
6098 else {
6099 if (cmd < 0)
6100 break;
6101 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
6102 break;
6105 newpassword2[1] = '\0';
6106 newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
6107 if (cmd == '#')
6108 newpassword2[0] = '\0';
6109 else {
6110 if (cmd < 0)
6111 break;
6113 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
6114 break;
6117 if (strcmp(newpassword, newpassword2)) {
6118 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
6119 cmd = ast_play_and_wait(chan, "vm-mismatch");
6120 break;
6122 if (ast_strlen_zero(ext_pass_cmd))
6123 vm_change_password(vmu,newpassword);
6124 else
6125 vm_change_password_shell(vmu,newpassword);
6126 if (option_debug > 2)
6127 ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
6128 cmd = ast_play_and_wait(chan,"vm-passchanged");
6129 break;
6130 case '*':
6131 cmd = 't';
6132 break;
6133 default:
6134 cmd = 0;
6135 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
6136 if (ast_fileexists(prefile, NULL, NULL))
6137 cmd = ast_play_and_wait(chan, "vm-tmpexists");
6138 if (!cmd)
6139 cmd = ast_play_and_wait(chan, "vm-options");
6140 if (!cmd)
6141 cmd = ast_waitfordigit(chan,6000);
6142 if (!cmd)
6143 retries++;
6144 if (retries > 3)
6145 cmd = 't';
6148 if (cmd == 't')
6149 cmd = 0;
6150 return cmd;
6153 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
6155 int res;
6156 int cmd = 0;
6157 int retries = 0;
6158 int duration = 0;
6159 char prefile[PATH_MAX] = "";
6160 unsigned char buf[256];
6161 char dest[PATH_MAX];
6162 int bytes = 0;
6164 if (ast_adsi_available(chan)) {
6165 bytes += adsi_logo(buf + bytes);
6166 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
6167 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
6168 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6169 bytes += ast_adsi_voice_mode(buf + bytes, 0);
6170 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6173 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
6174 if ((res = create_dirpath(dest, sizeof(dest), vmu->context, vms->username, "temp"))) {
6175 ast_log(LOG_WARNING, "Failed to create directory (%s).\n", prefile);
6176 return -1;
6178 while ((cmd >= 0) && (cmd != 't')) {
6179 if (cmd)
6180 retries = 0;
6181 RETRIEVE(prefile, -1);
6182 if (ast_fileexists(prefile, NULL, NULL) <= 0) {
6183 #ifndef IMAP_STORAGE
6184 play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6185 #else
6186 play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6187 #endif
6188 cmd = 't';
6189 } else {
6190 switch (cmd) {
6191 case '1':
6192 #ifndef IMAP_STORAGE
6193 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6194 #else
6195 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6196 #endif
6197 break;
6198 case '2':
6199 DELETE(prefile, -1, prefile);
6200 ast_play_and_wait(chan, "vm-tempremoved");
6201 cmd = 't';
6202 break;
6203 case '*':
6204 cmd = 't';
6205 break;
6206 default:
6207 cmd = ast_play_and_wait(chan,
6208 ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
6209 "vm-tempgreeting2" : "vm-tempgreeting");
6210 if (!cmd)
6211 cmd = ast_waitfordigit(chan,6000);
6212 if (!cmd)
6213 retries++;
6214 if (retries > 3)
6215 cmd = 't';
6218 DISPOSE(prefile, -1);
6220 if (cmd == 't')
6221 cmd = 0;
6222 return cmd;
6225 /* GREEK SYNTAX */
6227 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6229 int cmd=0;
6231 if (vms->lastmsg > -1) {
6232 cmd = play_message(chan, vmu, vms);
6233 } else {
6234 cmd = ast_play_and_wait(chan, "vm-youhaveno");
6235 if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
6236 if (!cmd) {
6237 snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
6238 cmd = ast_play_and_wait(chan, vms->fn);
6240 if (!cmd)
6241 cmd = ast_play_and_wait(chan, "vm-messages");
6242 } else {
6243 if (!cmd)
6244 cmd = ast_play_and_wait(chan, "vm-messages");
6245 if (!cmd) {
6246 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6247 cmd = ast_play_and_wait(chan, vms->fn);
6251 return cmd;
6254 /* Default English syntax */
6255 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6257 int cmd=0;
6259 if (vms->lastmsg > -1) {
6260 cmd = play_message(chan, vmu, vms);
6261 } else {
6262 cmd = ast_play_and_wait(chan, "vm-youhave");
6263 if (!cmd)
6264 cmd = ast_play_and_wait(chan, "vm-no");
6265 if (!cmd) {
6266 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6267 cmd = ast_play_and_wait(chan, vms->fn);
6269 if (!cmd)
6270 cmd = ast_play_and_wait(chan, "vm-messages");
6272 return cmd;
6275 /* ITALIAN syntax */
6276 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6278 int cmd=0;
6280 if (vms->lastmsg > -1) {
6281 cmd = play_message(chan, vmu, vms);
6282 } else {
6283 cmd = ast_play_and_wait(chan, "vm-no");
6284 if (!cmd)
6285 cmd = ast_play_and_wait(chan, "vm-message");
6286 if (!cmd) {
6287 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6288 cmd = ast_play_and_wait(chan, vms->fn);
6291 return cmd;
6294 /* SPANISH syntax */
6295 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6297 int cmd=0;
6299 if (vms->lastmsg > -1) {
6300 cmd = play_message(chan, vmu, vms);
6301 } else {
6302 cmd = ast_play_and_wait(chan, "vm-youhaveno");
6303 if (!cmd)
6304 cmd = ast_play_and_wait(chan, "vm-messages");
6305 if (!cmd) {
6306 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6307 cmd = ast_play_and_wait(chan, vms->fn);
6310 return cmd;
6313 /* PORTUGUESE syntax */
6314 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6316 int cmd=0;
6318 if (vms->lastmsg > -1) {
6319 cmd = play_message(chan, vmu, vms);
6320 } else {
6321 cmd = ast_play_and_wait(chan, "vm-no");
6322 if (!cmd) {
6323 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6324 cmd = ast_play_and_wait(chan, vms->fn);
6326 if (!cmd)
6327 cmd = ast_play_and_wait(chan, "vm-messages");
6329 return cmd;
6332 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6334 if (!strcasecmp(chan->language, "es")) { /* SPANISH */
6335 return vm_browse_messages_es(chan, vms, vmu);
6336 } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN */
6337 return vm_browse_messages_it(chan, vms, vmu);
6338 } else if (!strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* PORTUGUESE */
6339 return vm_browse_messages_pt(chan, vms, vmu);
6340 } else if (!strcasecmp(chan->language, "gr")){
6341 return vm_browse_messages_gr(chan, vms, vmu); /* GREEK */
6342 } else { /* Default to English syntax */
6343 return vm_browse_messages_en(chan, vms, vmu);
6347 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
6348 struct ast_vm_user *res_vmu, const char *context, const char *prefix,
6349 int skipuser, int maxlogins, int silent)
6351 int useadsi=0, valid=0, logretries=0;
6352 char password[AST_MAX_EXTENSION]="", *passptr;
6353 struct ast_vm_user vmus, *vmu = NULL;
6355 /* If ADSI is supported, setup login screen */
6356 adsi_begin(chan, &useadsi);
6357 if (!skipuser && useadsi)
6358 adsi_login(chan);
6359 if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
6360 ast_log(LOG_WARNING, "Couldn't stream login file\n");
6361 return -1;
6364 /* Authenticate them and get their mailbox/password */
6366 while (!valid && (logretries < maxlogins)) {
6367 /* Prompt for, and read in the username */
6368 if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
6369 ast_log(LOG_WARNING, "Couldn't read username\n");
6370 return -1;
6372 if (ast_strlen_zero(mailbox)) {
6373 if (chan->cid.cid_num) {
6374 ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
6375 } else {
6376 if (option_verbose > 2)
6377 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
6378 return -1;
6381 if (useadsi)
6382 adsi_password(chan);
6384 if (!ast_strlen_zero(prefix)) {
6385 char fullusername[80] = "";
6386 ast_copy_string(fullusername, prefix, sizeof(fullusername));
6387 strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
6388 ast_copy_string(mailbox, fullusername, mailbox_size);
6391 if (option_debug)
6392 ast_log(LOG_DEBUG, "Before find user for mailbox %s\n",mailbox);
6393 vmu = find_user(&vmus, context, mailbox);
6394 if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
6395 /* saved password is blank, so don't bother asking */
6396 password[0] = '\0';
6397 } else {
6398 if (ast_streamfile(chan, "vm-password", chan->language)) {
6399 ast_log(LOG_WARNING, "Unable to stream password file\n");
6400 return -1;
6402 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
6403 ast_log(LOG_WARNING, "Unable to read password\n");
6404 return -1;
6408 if (vmu) {
6409 passptr = vmu->password;
6410 if (passptr[0] == '-') passptr++;
6412 if (vmu && !strcmp(passptr, password))
6413 valid++;
6414 else {
6415 if (option_verbose > 2)
6416 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
6417 if (!ast_strlen_zero(prefix))
6418 mailbox[0] = '\0';
6420 logretries++;
6421 if (!valid) {
6422 if (skipuser || logretries >= maxlogins) {
6423 if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
6424 ast_log(LOG_WARNING, "Unable to stream incorrect message\n");
6425 return -1;
6427 } else {
6428 if (useadsi)
6429 adsi_login(chan);
6430 if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
6431 ast_log(LOG_WARNING, "Unable to stream incorrect mailbox message\n");
6432 return -1;
6435 if (ast_waitstream(chan, "")) /* Channel is hung up */
6436 return -1;
6439 if (!valid && (logretries >= maxlogins)) {
6440 ast_stopstream(chan);
6441 ast_play_and_wait(chan, "vm-goodbye");
6442 return -1;
6444 if (vmu && !skipuser) {
6445 memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
6447 return 0;
6450 static int vm_execmain(struct ast_channel *chan, void *data)
6452 /* XXX This is, admittedly, some pretty horrendus code. For some
6453 reason it just seemed a lot easier to do with GOTO's. I feel
6454 like I'm back in my GWBASIC days. XXX */
6455 int res=-1;
6456 int cmd=0;
6457 int valid = 0;
6458 struct ast_module_user *u;
6459 char prefixstr[80] ="";
6460 char ext_context[256]="";
6461 int box;
6462 int useadsi = 0;
6463 int skipuser = 0;
6464 struct vm_state vms;
6465 struct ast_vm_user *vmu = NULL, vmus;
6466 char *context=NULL;
6467 int silentexit = 0;
6468 struct ast_flags flags = { 0 };
6469 signed char record_gain = 0;
6470 int play_auto = 0;
6471 int play_folder = 0;
6472 #ifdef IMAP_STORAGE
6473 int deleted = 0;
6474 #endif
6475 u = ast_module_user_add(chan);
6477 /* Add the vm_state to the active list and keep it active */
6478 memset(&vms, 0, sizeof(vms));
6479 vms.lastmsg = -1;
6481 memset(&vmus, 0, sizeof(vmus));
6483 if (chan->_state != AST_STATE_UP) {
6484 if (option_debug)
6485 ast_log(LOG_DEBUG, "Before ast_answer\n");
6486 ast_answer(chan);
6489 if (!ast_strlen_zero(data)) {
6490 char *opts[OPT_ARG_ARRAY_SIZE];
6491 char *parse;
6492 AST_DECLARE_APP_ARGS(args,
6493 AST_APP_ARG(argv0);
6494 AST_APP_ARG(argv1);
6497 parse = ast_strdupa(data);
6499 AST_STANDARD_APP_ARGS(args, parse);
6501 if (args.argc == 2) {
6502 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
6503 ast_module_user_remove(u);
6504 return -1;
6506 if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
6507 int gain;
6508 if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
6509 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
6510 ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
6511 ast_module_user_remove(u);
6512 return -1;
6513 } else {
6514 record_gain = (signed char) gain;
6516 } else {
6517 ast_log(LOG_WARNING, "Invalid Gain level set with option g\n");
6520 if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
6521 play_auto = 1;
6522 if (opts[OPT_ARG_PLAYFOLDER]) {
6523 if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%d", &play_folder) != 1) {
6524 ast_log(LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
6526 } else {
6527 ast_log(LOG_WARNING, "Invalid folder set with option a\n");
6529 if ( play_folder > 9 || play_folder < 0) {
6530 ast_log(LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
6531 play_folder = 0;
6534 } else {
6535 /* old style options parsing */
6536 while (*(args.argv0)) {
6537 if (*(args.argv0) == 's')
6538 ast_set_flag(&flags, OPT_SILENT);
6539 else if (*(args.argv0) == 'p')
6540 ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
6541 else
6542 break;
6543 (args.argv0)++;
6548 valid = ast_test_flag(&flags, OPT_SILENT);
6550 if ((context = strchr(args.argv0, '@')))
6551 *context++ = '\0';
6553 if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
6554 ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
6555 else
6556 ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
6558 if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
6559 skipuser++;
6560 else
6561 valid = 0;
6564 if (!valid)
6565 res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
6567 if (option_debug)
6568 ast_log(LOG_DEBUG, "After vm_authenticate\n");
6569 if (!res) {
6570 valid = 1;
6571 if (!skipuser)
6572 vmu = &vmus;
6573 } else {
6574 res = 0;
6577 /* If ADSI is supported, setup login screen */
6578 adsi_begin(chan, &useadsi);
6580 #ifdef IMAP_STORAGE
6581 vms.interactive = 1;
6582 vms.updated = 1;
6583 vmstate_insert(&vms);
6584 init_vm_state(&vms);
6585 #endif
6586 if (!valid)
6587 goto out;
6589 if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
6590 /* TODO: Handle memory allocation failure */
6592 if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
6593 /* TODO: Handle memory allocation failure */
6596 /* Set language from config to override channel language */
6597 if (!ast_strlen_zero(vmu->language))
6598 ast_string_field_set(chan, language, vmu->language);
6599 create_dirpath(vms.curdir, sizeof(vms.curdir), vmu->context, vms.username, "");
6600 /* Retrieve old and new message counts */
6601 if (option_debug)
6602 ast_log(LOG_DEBUG, "Before open_mailbox\n");
6603 res = open_mailbox(&vms, vmu, 1);
6604 if (res == ERROR_LOCK_PATH)
6605 goto out;
6606 vms.oldmessages = vms.lastmsg + 1;
6607 if (option_debug > 2)
6608 ast_log(LOG_DEBUG, "Number of old messages: %d\n",vms.oldmessages);
6609 /* Start in INBOX */
6610 res = open_mailbox(&vms, vmu, 0);
6611 if (res == ERROR_LOCK_PATH)
6612 goto out;
6613 vms.newmessages = vms.lastmsg + 1;
6614 if (option_debug > 2)
6615 ast_log(LOG_DEBUG, "Number of new messages: %d\n",vms.newmessages);
6617 /* Select proper mailbox FIRST!! */
6618 if (play_auto) {
6619 res = open_mailbox(&vms, vmu, play_folder);
6620 if (res == ERROR_LOCK_PATH)
6621 goto out;
6623 /* If there are no new messages, inform the user and hangup */
6624 if (vms.lastmsg == -1) {
6625 cmd = vm_browse_messages(chan, &vms, vmu);
6626 res = 0;
6627 goto out;
6629 } else {
6630 if (!vms.newmessages && vms.oldmessages) {
6631 /* If we only have old messages start here */
6632 res = open_mailbox(&vms, vmu, 1);
6633 play_folder = 1;
6634 if (res == ERROR_LOCK_PATH)
6635 goto out;
6639 if (useadsi)
6640 adsi_status(chan, &vms);
6641 res = 0;
6643 /* Check to see if this is a new user */
6644 if (!strcasecmp(vmu->mailbox, vmu->password) &&
6645 (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
6646 if (ast_play_and_wait(chan, "vm-newuser") == -1)
6647 ast_log(LOG_WARNING, "Couldn't stream new user file\n");
6648 cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
6649 if ((cmd == 't') || (cmd == '#')) {
6650 /* Timeout */
6651 res = 0;
6652 goto out;
6653 } else if (cmd < 0) {
6654 /* Hangup */
6655 res = -1;
6656 goto out;
6659 #ifdef IMAP_STORAGE
6660 if (option_debug > 2)
6661 ast_log(LOG_DEBUG, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
6662 if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
6663 if (option_debug)
6664 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!!\n");
6665 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
6667 if (option_debug > 2)
6668 ast_log(LOG_DEBUG, "Checking quotas: User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
6669 if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
6670 ast_log(LOG_WARNING, "No more messages possible. User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
6671 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
6673 #endif
6674 if (play_auto) {
6675 cmd = '1';
6676 } else {
6677 cmd = vm_intro(chan, vmu, &vms);
6680 vms.repeats = 0;
6681 vms.starting = 1;
6682 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
6683 /* Run main menu */
6684 switch (cmd) {
6685 case '1':
6686 vms.curmsg = 0;
6687 /* Fall through */
6688 case '5':
6689 cmd = vm_browse_messages(chan, &vms, vmu);
6690 break;
6691 case '2': /* Change folders */
6692 if (useadsi)
6693 adsi_folders(chan, 0, "Change to folder...");
6694 cmd = get_folder2(chan, "vm-changeto", 0);
6695 if (cmd == '#') {
6696 cmd = 0;
6697 } else if (cmd > 0) {
6698 cmd = cmd - '0';
6699 res = close_mailbox(&vms, vmu);
6700 if (res == ERROR_LOCK_PATH)
6701 goto out;
6702 res = open_mailbox(&vms, vmu, cmd);
6703 if (res == ERROR_LOCK_PATH)
6704 goto out;
6705 play_folder = cmd;
6706 cmd = 0;
6708 if (useadsi)
6709 adsi_status2(chan, &vms);
6711 if (!cmd)
6712 cmd = vm_play_folder_name(chan, vms.vmbox);
6714 vms.starting = 1;
6715 break;
6716 case '3': /* Advanced options */
6717 cmd = 0;
6718 vms.repeats = 0;
6719 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
6720 switch (cmd) {
6721 case '1': /* Reply */
6722 if (vms.lastmsg > -1 && !vms.starting) {
6723 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
6724 if (cmd == ERROR_LOCK_PATH) {
6725 res = cmd;
6726 goto out;
6728 } else
6729 cmd = ast_play_and_wait(chan, "vm-sorry");
6730 cmd = 't';
6731 break;
6732 case '2': /* Callback */
6733 if (option_verbose > 2 && !vms.starting)
6734 ast_verbose( VERBOSE_PREFIX_3 "Callback Requested\n");
6735 if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
6736 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
6737 if (cmd == 9) {
6738 silentexit = 1;
6739 goto out;
6740 } else if (cmd == ERROR_LOCK_PATH) {
6741 res = cmd;
6742 goto out;
6745 else
6746 cmd = ast_play_and_wait(chan, "vm-sorry");
6747 cmd = 't';
6748 break;
6749 case '3': /* Envelope */
6750 if (vms.lastmsg > -1 && !vms.starting) {
6751 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
6752 if (cmd == ERROR_LOCK_PATH) {
6753 res = cmd;
6754 goto out;
6756 } else
6757 cmd = ast_play_and_wait(chan, "vm-sorry");
6758 cmd = 't';
6759 break;
6760 case '4': /* Dialout */
6761 if (!ast_strlen_zero(vmu->dialout)) {
6762 cmd = dialout(chan, vmu, NULL, vmu->dialout);
6763 if (cmd == 9) {
6764 silentexit = 1;
6765 goto out;
6768 else
6769 cmd = ast_play_and_wait(chan, "vm-sorry");
6770 cmd = 't';
6771 break;
6773 case '5': /* Leave VoiceMail */
6774 if (ast_test_flag(vmu, VM_SVMAIL)) {
6775 cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain);
6776 if (cmd == ERROR_LOCK_PATH) {
6777 res = cmd;
6778 ast_log(LOG_WARNING, "forward_message failed to lock path.\n");
6779 goto out;
6781 } else
6782 cmd = ast_play_and_wait(chan,"vm-sorry");
6783 cmd='t';
6784 break;
6786 case '*': /* Return to main menu */
6787 cmd = 't';
6788 break;
6790 default:
6791 cmd = 0;
6792 if (!vms.starting) {
6793 cmd = ast_play_and_wait(chan, "vm-toreply");
6795 if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
6796 cmd = ast_play_and_wait(chan, "vm-tocallback");
6798 if (!cmd && !vms.starting) {
6799 cmd = ast_play_and_wait(chan, "vm-tohearenv");
6801 if (!ast_strlen_zero(vmu->dialout) && !cmd) {
6802 cmd = ast_play_and_wait(chan, "vm-tomakecall");
6804 if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
6805 cmd=ast_play_and_wait(chan, "vm-leavemsg");
6806 if (!cmd)
6807 cmd = ast_play_and_wait(chan, "vm-starmain");
6808 if (!cmd)
6809 cmd = ast_waitfordigit(chan,6000);
6810 if (!cmd)
6811 vms.repeats++;
6812 if (vms.repeats > 3)
6813 cmd = 't';
6816 if (cmd == 't') {
6817 cmd = 0;
6818 vms.repeats = 0;
6820 break;
6821 case '4':
6822 if (vms.curmsg > 0) {
6823 vms.curmsg--;
6824 cmd = play_message(chan, vmu, &vms);
6825 } else {
6826 cmd = ast_play_and_wait(chan, "vm-nomore");
6828 break;
6829 case '6':
6830 if (vms.curmsg < vms.lastmsg) {
6831 vms.curmsg++;
6832 cmd = play_message(chan, vmu, &vms);
6833 } else {
6834 cmd = ast_play_and_wait(chan, "vm-nomore");
6836 break;
6837 case '7':
6838 if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
6839 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
6840 if (useadsi)
6841 adsi_delete(chan, &vms);
6842 if (vms.deleted[vms.curmsg]) {
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-deleted");
6849 else {
6850 if (play_folder == 0)
6851 vms.newmessages++;
6852 else if (play_folder == 1)
6853 vms.oldmessages++;
6854 cmd = ast_play_and_wait(chan, "vm-undeleted");
6856 if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
6857 if (vms.curmsg < vms.lastmsg) {
6858 vms.curmsg++;
6859 cmd = play_message(chan, vmu, &vms);
6860 } else {
6861 cmd = ast_play_and_wait(chan, "vm-nomore");
6864 } else /* Delete not valid if we haven't selected a message */
6865 cmd = 0;
6866 #ifdef IMAP_STORAGE
6867 deleted = 1;
6868 #endif
6869 break;
6871 case '8':
6872 if (vms.lastmsg > -1) {
6873 cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain);
6874 if (cmd == ERROR_LOCK_PATH) {
6875 res = cmd;
6876 goto out;
6878 } else
6879 cmd = ast_play_and_wait(chan, "vm-nomore");
6880 break;
6881 case '9':
6882 if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
6883 /* No message selected */
6884 cmd = 0;
6885 break;
6887 if (useadsi)
6888 adsi_folders(chan, 1, "Save to folder...");
6889 cmd = get_folder2(chan, "vm-savefolder", 1);
6890 box = 0; /* Shut up compiler */
6891 if (cmd == '#') {
6892 cmd = 0;
6893 break;
6894 } else if (cmd > 0) {
6895 box = cmd = cmd - '0';
6896 cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
6897 if (cmd == ERROR_LOCK_PATH) {
6898 res = cmd;
6899 goto out;
6900 #ifdef IMAP_STORAGE
6901 } else if (cmd == 10) {
6902 goto out;
6903 #endif
6904 } else if (!cmd) {
6905 vms.deleted[vms.curmsg] = 1;
6906 } else {
6907 vms.deleted[vms.curmsg] = 0;
6908 vms.heard[vms.curmsg] = 0;
6911 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
6912 if (useadsi)
6913 adsi_message(chan, &vms);
6914 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
6915 if (!cmd) {
6916 cmd = ast_play_and_wait(chan, "vm-message");
6917 if (!cmd)
6918 cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
6919 if (!cmd)
6920 cmd = ast_play_and_wait(chan, "vm-savedto");
6921 if (!cmd)
6922 cmd = vm_play_folder_name(chan, vms.fn);
6923 } else {
6924 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
6926 if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
6927 if (vms.curmsg < vms.lastmsg) {
6928 vms.curmsg++;
6929 cmd = play_message(chan, vmu, &vms);
6930 } else {
6931 cmd = ast_play_and_wait(chan, "vm-nomore");
6934 break;
6935 case '*':
6936 if (!vms.starting) {
6937 cmd = ast_play_and_wait(chan, "vm-onefor");
6938 if (!cmd)
6939 cmd = vm_play_folder_name(chan, vms.vmbox);
6940 if (!cmd)
6941 cmd = ast_play_and_wait(chan, "vm-opts");
6942 if (!cmd)
6943 cmd = vm_instructions(chan, &vms, 1);
6944 } else
6945 cmd = 0;
6946 break;
6947 case '0':
6948 cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
6949 if (useadsi)
6950 adsi_status(chan, &vms);
6951 break;
6952 default: /* Nothing */
6953 cmd = vm_instructions(chan, &vms, 0);
6954 break;
6957 if ((cmd == 't') || (cmd == '#')) {
6958 /* Timeout */
6959 res = 0;
6960 } else {
6961 /* Hangup */
6962 res = -1;
6965 out:
6966 if (res > -1) {
6967 ast_stopstream(chan);
6968 adsi_goodbye(chan);
6969 if (valid) {
6970 if (silentexit)
6971 res = ast_play_and_wait(chan, "vm-dialout");
6972 else
6973 res = ast_play_and_wait(chan, "vm-goodbye");
6974 if (res > 0)
6975 res = 0;
6977 if (useadsi)
6978 ast_adsi_unload_session(chan);
6980 if (vmu)
6981 close_mailbox(&vms, vmu);
6982 if (valid) {
6983 snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
6984 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
6985 run_externnotify(vmu->context, vmu->mailbox);
6987 #ifdef IMAP_STORAGE
6988 /* expunge message - use UID Expunge if supported on IMAP server*/
6989 if (option_debug > 2)
6990 ast_log(LOG_DEBUG, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
6991 if (vmu && deleted == 1 && expungeonhangup == 1) {
6992 #ifdef HAVE_IMAP_TK2006
6993 if (LEVELUIDPLUS (vms.mailstream)) {
6994 mail_expunge_full(vms.mailstream,NIL,EX_UID);
6995 } else
6996 #endif
6997 mail_expunge(vms.mailstream);
6999 /* before we delete the state, we should copy pertinent info
7000 * back to the persistent model */
7001 vmstate_delete(&vms);
7002 #endif
7003 if (vmu)
7004 free_user(vmu);
7005 if (vms.deleted)
7006 free(vms.deleted);
7007 if (vms.heard)
7008 free(vms.heard);
7009 ast_module_user_remove(u);
7011 return res;
7014 static int vm_exec(struct ast_channel *chan, void *data)
7016 int res = 0;
7017 struct ast_module_user *u;
7018 char *tmp;
7019 struct leave_vm_options leave_options;
7020 struct ast_flags flags = { 0 };
7021 static int deprecate_warning = 0;
7022 char *opts[OPT_ARG_ARRAY_SIZE];
7023 AST_DECLARE_APP_ARGS(args,
7024 AST_APP_ARG(argv0);
7025 AST_APP_ARG(argv1);
7028 u = ast_module_user_add(chan);
7030 memset(&leave_options, 0, sizeof(leave_options));
7032 if (chan->_state != AST_STATE_UP)
7033 ast_answer(chan);
7035 if (!ast_strlen_zero(data)) {
7036 tmp = ast_strdupa(data);
7037 AST_STANDARD_APP_ARGS(args, tmp);
7038 if (args.argc == 2) {
7039 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
7040 ast_module_user_remove(u);
7041 return -1;
7043 ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_PRIORITY_JUMP);
7044 if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
7045 int gain;
7047 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
7048 ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
7049 ast_module_user_remove(u);
7050 return -1;
7051 } else {
7052 leave_options.record_gain = (signed char) gain;
7055 } else {
7056 /* old style options parsing */
7057 int old = 0;
7058 char *orig_argv0 = args.argv0;
7059 while (*(args.argv0)) {
7060 if (*(args.argv0) == 's') {
7061 old = 1;
7062 ast_set_flag(&leave_options, OPT_SILENT);
7063 } else if (*(args.argv0) == 'b') {
7064 old = 1;
7065 ast_set_flag(&leave_options, OPT_BUSY_GREETING);
7066 } else if (*(args.argv0) == 'u') {
7067 old = 1;
7068 ast_set_flag(&leave_options, OPT_UNAVAIL_GREETING);
7069 } else if (*(args.argv0) == 'j') {
7070 old = 1;
7071 ast_set_flag(&leave_options, OPT_PRIORITY_JUMP);
7072 } else
7073 break;
7074 (args.argv0)++;
7076 if (!deprecate_warning && old) {
7077 deprecate_warning = 1;
7078 ast_log(LOG_WARNING, "Prefixing the mailbox with an option is deprecated ('%s').\n", orig_argv0);
7079 ast_log(LOG_WARNING, "Please move all leading options to the second argument.\n");
7082 } else {
7083 char tmp[256];
7084 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
7085 if (res < 0) {
7086 ast_module_user_remove(u);
7087 return res;
7089 if (ast_strlen_zero(tmp)) {
7090 ast_module_user_remove(u);
7091 return 0;
7093 args.argv0 = ast_strdupa(tmp);
7096 res = leave_voicemail(chan, args.argv0, &leave_options);
7098 if (res == ERROR_LOCK_PATH) {
7099 ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
7100 /*Send the call to n+101 priority, where n is the current priority*/
7101 if (ast_test_flag(&leave_options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
7102 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
7103 ast_log(LOG_WARNING, "Extension %s, priority %d doesn't exist.\n", chan->exten, chan->priority + 101);
7104 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7105 res = 0;
7108 ast_module_user_remove(u);
7110 return res;
7113 static struct ast_vm_user *find_or_create(char *context, char *mbox)
7115 struct ast_vm_user *vmu;
7116 AST_LIST_TRAVERSE(&users, vmu, list) {
7117 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mbox, vmu->mailbox))
7118 break;
7119 if (context && (!strcasecmp(context, vmu->context)) && (!strcasecmp(mbox, vmu->mailbox)))
7120 break;
7123 if (!vmu) {
7124 if ((vmu = ast_calloc(1, sizeof(*vmu)))) {
7125 ast_copy_string(vmu->context, context, sizeof(vmu->context));
7126 ast_copy_string(vmu->mailbox, mbox, sizeof(vmu->mailbox));
7127 AST_LIST_INSERT_TAIL(&users, vmu, list);
7130 return vmu;
7133 static int append_mailbox(char *context, char *mbox, char *data)
7135 /* Assumes lock is already held */
7136 char *tmp;
7137 char *stringp;
7138 char *s;
7139 struct ast_vm_user *vmu;
7141 tmp = ast_strdupa(data);
7143 if ((vmu = find_or_create(context, mbox))) {
7144 populate_defaults(vmu);
7146 stringp = tmp;
7147 if ((s = strsep(&stringp, ",")))
7148 ast_copy_string(vmu->password, s, sizeof(vmu->password));
7149 if (stringp && (s = strsep(&stringp, ",")))
7150 ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
7151 if (stringp && (s = strsep(&stringp, ",")))
7152 ast_copy_string(vmu->email, s, sizeof(vmu->email));
7153 if (stringp && (s = strsep(&stringp, ",")))
7154 ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
7155 if (stringp && (s = strsep(&stringp, ",")))
7156 apply_options(vmu, s);
7158 return 0;
7161 static int vm_box_exists(struct ast_channel *chan, void *data)
7163 struct ast_module_user *u;
7164 struct ast_vm_user svm;
7165 char *context, *box;
7166 int priority_jump = 0;
7167 AST_DECLARE_APP_ARGS(args,
7168 AST_APP_ARG(mbox);
7169 AST_APP_ARG(options);
7172 if (ast_strlen_zero(data)) {
7173 ast_log(LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
7174 return -1;
7177 u = ast_module_user_add(chan);
7179 box = ast_strdupa(data);
7181 AST_STANDARD_APP_ARGS(args, box);
7183 if (args.options) {
7184 if (strchr(args.options, 'j'))
7185 priority_jump = 1;
7188 if ((context = strchr(args.mbox, '@'))) {
7189 *context = '\0';
7190 context++;
7193 if (find_user(&svm, context, args.mbox)) {
7194 pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
7195 if (priority_jump || ast_opt_priority_jumping)
7196 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
7197 ast_log(LOG_WARNING, "VM box %s@%s exists, but extension %s, priority %d doesn't exist\n", box, context, chan->exten, chan->priority + 101);
7198 } else
7199 pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
7200 ast_module_user_remove(u);
7201 return 0;
7204 static int vmauthenticate(struct ast_channel *chan, void *data)
7206 struct ast_module_user *u;
7207 char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
7208 struct ast_vm_user vmus;
7209 char *options = NULL;
7210 int silent = 0, skipuser = 0;
7211 int res = -1;
7213 u = ast_module_user_add(chan);
7215 if (s) {
7216 s = ast_strdupa(s);
7217 user = strsep(&s, "|");
7218 options = strsep(&s, "|");
7219 if (user) {
7220 s = user;
7221 user = strsep(&s, "@");
7222 context = strsep(&s, "");
7223 if (!ast_strlen_zero(user))
7224 skipuser++;
7225 ast_copy_string(mailbox, user, sizeof(mailbox));
7229 if (options) {
7230 silent = (strchr(options, 's')) != NULL;
7233 if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
7234 pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
7235 pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
7236 ast_play_and_wait(chan, "auth-thankyou");
7237 res = 0;
7240 ast_module_user_remove(u);
7241 return res;
7244 static char voicemail_show_users_help[] =
7245 "Usage: voicemail show users [for <context>]\n"
7246 " Lists all mailboxes currently set up\n";
7248 static char voicemail_show_zones_help[] =
7249 "Usage: voicemail show zones\n"
7250 " Lists zone message formats\n";
7252 static int handle_voicemail_show_users(int fd, int argc, char *argv[])
7254 struct ast_vm_user *vmu;
7255 char *output_format = "%-10s %-5s %-25s %-10s %6s\n";
7257 if ((argc < 3) || (argc > 5) || (argc == 4)) return RESULT_SHOWUSAGE;
7258 else if ((argc == 5) && strcmp(argv[3],"for")) return RESULT_SHOWUSAGE;
7260 AST_LIST_LOCK(&users);
7261 if (!AST_LIST_EMPTY(&users)) {
7262 if (argc == 3)
7263 ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
7264 else {
7265 int count = 0;
7266 AST_LIST_TRAVERSE(&users, vmu, list) {
7267 if (!strcmp(argv[4],vmu->context))
7268 count++;
7270 if (count) {
7271 ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
7272 } else {
7273 ast_cli(fd, "No such voicemail context \"%s\"\n", argv[4]);
7274 AST_LIST_UNLOCK(&users);
7275 return RESULT_FAILURE;
7278 AST_LIST_TRAVERSE(&users, vmu, list) {
7279 int newmsgs = 0, oldmsgs = 0;
7280 char count[12], tmp[256] = "";
7282 if ((argc == 3) || ((argc == 5) && !strcmp(argv[4],vmu->context))) {
7283 snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
7284 inboxcount(tmp, &newmsgs, &oldmsgs);
7285 snprintf(count,sizeof(count),"%d",newmsgs);
7286 ast_cli(fd, output_format, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
7289 } else {
7290 ast_cli(fd, "There are no voicemail users currently defined\n");
7291 AST_LIST_UNLOCK(&users);
7292 return RESULT_FAILURE;
7294 AST_LIST_UNLOCK(&users);
7295 return RESULT_SUCCESS;
7298 static int handle_voicemail_show_zones(int fd, int argc, char *argv[])
7300 struct vm_zone *zone;
7301 char *output_format = "%-15s %-20s %-45s\n";
7302 int res = RESULT_SUCCESS;
7304 if (argc != 3)
7305 return RESULT_SHOWUSAGE;
7307 AST_LIST_LOCK(&zones);
7308 if (!AST_LIST_EMPTY(&zones)) {
7309 ast_cli(fd, output_format, "Zone", "Timezone", "Message Format");
7310 AST_LIST_TRAVERSE(&zones, zone, list) {
7311 ast_cli(fd, output_format, zone->name, zone->timezone, zone->msg_format);
7313 } else {
7314 ast_cli(fd, "There are no voicemail zones currently defined\n");
7315 res = RESULT_FAILURE;
7317 AST_LIST_UNLOCK(&zones);
7319 return res;
7322 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
7324 int which = 0;
7325 int wordlen;
7326 struct ast_vm_user *vmu;
7327 const char *context = "";
7329 /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
7330 if (pos > 4)
7331 return NULL;
7332 if (pos == 3)
7333 return (state == 0) ? ast_strdup("for") : NULL;
7334 wordlen = strlen(word);
7335 AST_LIST_TRAVERSE(&users, vmu, list) {
7336 if (!strncasecmp(word, vmu->context, wordlen)) {
7337 if (context && strcmp(context, vmu->context) && ++which > state)
7338 return ast_strdup(vmu->context);
7339 /* ignore repeated contexts ? */
7340 context = vmu->context;
7343 return NULL;
7346 static struct ast_cli_entry cli_show_voicemail_users_deprecated = {
7347 { "show", "voicemail", "users", NULL },
7348 handle_voicemail_show_users, NULL,
7349 NULL, complete_voicemail_show_users };
7351 static struct ast_cli_entry cli_show_voicemail_zones_deprecated = {
7352 { "show", "voicemail", "zones", NULL },
7353 handle_voicemail_show_zones, NULL,
7354 NULL, NULL };
7356 static struct ast_cli_entry cli_voicemail[] = {
7357 { { "voicemail", "show", "users", NULL },
7358 handle_voicemail_show_users, "List defined voicemail boxes",
7359 voicemail_show_users_help, complete_voicemail_show_users, &cli_show_voicemail_users_deprecated },
7361 { { "voicemail", "show", "zones", NULL },
7362 handle_voicemail_show_zones, "List zone message formats",
7363 voicemail_show_zones_help, NULL, &cli_show_voicemail_zones_deprecated },
7366 static int load_config(void)
7368 struct ast_vm_user *cur;
7369 struct vm_zone *zcur;
7370 struct ast_config *cfg, *ucfg;
7371 char *cat;
7372 struct ast_variable *var;
7373 const char *notifystr = NULL;
7374 const char *smdistr = NULL;
7375 const char *astattach;
7376 const char *astsearch;
7377 const char *astsaycid;
7378 const char *send_voicemail;
7379 #ifdef IMAP_STORAGE
7380 const char *imap_server;
7381 const char *imap_port;
7382 const char *imap_flags;
7383 const char *imap_folder;
7384 const char *auth_user;
7385 const char *auth_password;
7386 const char *expunge_on_hangup;
7387 #endif
7388 const char *astcallop;
7389 const char *astreview;
7390 const char *asttempgreetwarn;
7391 const char *astskipcmd;
7392 const char *asthearenv;
7393 const char *astsaydurationinfo;
7394 const char *astsaydurationminfo;
7395 const char *silencestr;
7396 const char *maxmsgstr;
7397 const char *astdirfwd;
7398 const char *thresholdstr;
7399 const char *fmt;
7400 const char *astemail;
7401 const char *ucontext;
7402 const char *astmailcmd = SENDMAIL;
7403 const char *astforcename;
7404 const char *astforcegreet;
7405 const char *s;
7406 char *q,*stringp;
7407 const char *dialoutcxt = NULL;
7408 const char *callbackcxt = NULL;
7409 const char *exitcxt = NULL;
7410 const char *extpc;
7411 const char *emaildateformatstr;
7412 const char *volgainstr;
7413 int x;
7414 int tmpadsi[4];
7416 cfg = ast_config_load(VOICEMAIL_CONFIG);
7418 AST_LIST_LOCK(&users);
7419 while ((cur = AST_LIST_REMOVE_HEAD(&users, list))) {
7420 ast_set_flag(cur, VM_ALLOCED);
7421 free_user(cur);
7424 AST_LIST_LOCK(&zones);
7425 while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
7426 free_zone(zcur);
7427 AST_LIST_UNLOCK(&zones);
7429 memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
7431 if (cfg) {
7432 /* General settings */
7434 if (!(ucontext = ast_variable_retrieve(cfg, "general", "userscontext")))
7435 ucontext = "default";
7436 ast_copy_string(userscontext, ucontext, sizeof(userscontext));
7437 /* Attach voice message to mail message ? */
7438 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach")))
7439 astattach = "yes";
7440 ast_set2_flag((&globalflags), ast_true(astattach), VM_ATTACH);
7442 if (!(astsearch = ast_variable_retrieve(cfg, "general", "searchcontexts")))
7443 astsearch = "no";
7444 ast_set2_flag((&globalflags), ast_true(astsearch), VM_SEARCH);
7446 volgain = 0.0;
7447 if ((volgainstr = ast_variable_retrieve(cfg, "general", "volgain")))
7448 sscanf(volgainstr, "%lf", &volgain);
7450 #ifdef ODBC_STORAGE
7451 strcpy(odbc_database, "asterisk");
7452 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
7453 ast_copy_string(odbc_database, thresholdstr, sizeof(odbc_database));
7455 strcpy(odbc_table, "voicemessages");
7456 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbctable"))) {
7457 ast_copy_string(odbc_table, thresholdstr, sizeof(odbc_table));
7459 #endif
7460 /* Mail command */
7461 strcpy(mailcmd, SENDMAIL);
7462 if ((astmailcmd = ast_variable_retrieve(cfg, "general", "mailcmd")))
7463 ast_copy_string(mailcmd, astmailcmd, sizeof(mailcmd)); /* User setting */
7465 maxsilence = 0;
7466 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
7467 maxsilence = atoi(silencestr);
7468 if (maxsilence > 0)
7469 maxsilence *= 1000;
7472 if (!(maxmsgstr = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
7473 maxmsg = MAXMSG;
7474 } else {
7475 maxmsg = atoi(maxmsgstr);
7476 if (maxmsg <= 0) {
7477 ast_log(LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", maxmsgstr, MAXMSG);
7478 maxmsg = MAXMSG;
7479 } else if (maxmsg > MAXMSGLIMIT) {
7480 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, maxmsgstr);
7481 maxmsg = MAXMSGLIMIT;
7485 /* Load date format config for voicemail mail */
7486 if ((emaildateformatstr = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
7487 ast_copy_string(emaildateformat, emaildateformatstr, sizeof(emaildateformat));
7490 /* External password changing command */
7491 if ((extpc = ast_variable_retrieve(cfg, "general", "externpass"))) {
7492 ast_copy_string(ext_pass_cmd,extpc,sizeof(ext_pass_cmd));
7494 #ifdef IMAP_STORAGE
7495 /* IMAP server address */
7496 if ((imap_server = ast_variable_retrieve(cfg, "general", "imapserver"))) {
7497 ast_copy_string(imapserver, imap_server, sizeof(imapserver));
7498 } else {
7499 ast_copy_string(imapserver,"localhost", sizeof(imapserver));
7501 /* IMAP server port */
7502 if ((imap_port = ast_variable_retrieve(cfg, "general", "imapport"))) {
7503 ast_copy_string(imapport, imap_port, sizeof(imapport));
7504 } else {
7505 ast_copy_string(imapport,"143", sizeof(imapport));
7507 /* IMAP server flags */
7508 if ((imap_flags = ast_variable_retrieve(cfg, "general", "imapflags"))) {
7509 ast_copy_string(imapflags, imap_flags, sizeof(imapflags));
7511 /* IMAP server master username */
7512 if ((auth_user = ast_variable_retrieve(cfg, "general", "authuser"))) {
7513 ast_copy_string(authuser, auth_user, sizeof(authuser));
7515 /* IMAP server master password */
7516 if ((auth_password = ast_variable_retrieve(cfg, "general", "authpassword"))) {
7517 ast_copy_string(authpassword, auth_password, sizeof(authpassword));
7519 /* Expunge on exit */
7520 if ((expunge_on_hangup = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
7521 if (ast_false(expunge_on_hangup))
7522 expungeonhangup = 0;
7523 else
7524 expungeonhangup = 1;
7525 } else {
7526 expungeonhangup = 1;
7528 /* IMAP voicemail folder */
7529 if ((imap_folder = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
7530 ast_copy_string(imapfolder, imap_folder, sizeof(imapfolder));
7531 } else {
7532 ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
7534 #endif
7535 /* External voicemail notify application */
7537 if ((notifystr = ast_variable_retrieve(cfg, "general", "externnotify"))) {
7538 ast_copy_string(externnotify, notifystr, sizeof(externnotify));
7539 if (option_debug > 2)
7540 ast_log(LOG_DEBUG, "found externnotify: %s\n", externnotify);
7541 if (!strcasecmp(externnotify, "smdi")) {
7542 if (option_debug)
7543 ast_log(LOG_DEBUG, "Using SMDI for external voicemail notification\n");
7544 if ((smdistr = ast_variable_retrieve(cfg, "general", "smdiport"))) {
7545 smdi_iface = ast_smdi_interface_find(smdistr);
7546 } else {
7547 if (option_debug)
7548 ast_log(LOG_DEBUG, "No SMDI interface set, trying default (/dev/ttyS0)\n");
7549 smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
7552 if (!smdi_iface) {
7553 ast_log(LOG_ERROR, "No valid SMDI interface specfied, disabling external voicemail notification\n");
7554 externnotify[0] = '\0';
7557 } else {
7558 externnotify[0] = '\0';
7561 /* Silence treshold */
7562 silencethreshold = 256;
7563 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
7564 silencethreshold = atoi(thresholdstr);
7566 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
7567 astemail = ASTERISK_USERNAME;
7568 ast_copy_string(serveremail, astemail, sizeof(serveremail));
7570 vmmaxmessage = 0;
7571 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
7572 if (sscanf(s, "%d", &x) == 1) {
7573 vmmaxmessage = x;
7574 } else {
7575 ast_log(LOG_WARNING, "Invalid max message time length\n");
7579 vmminmessage = 0;
7580 if ((s = ast_variable_retrieve(cfg, "general", "minmessage"))) {
7581 if (sscanf(s, "%d", &x) == 1) {
7582 vmminmessage = x;
7583 if (maxsilence <= vmminmessage)
7584 ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
7585 } else {
7586 ast_log(LOG_WARNING, "Invalid min message time length\n");
7589 fmt = ast_variable_retrieve(cfg, "general", "format");
7590 if (!fmt)
7591 fmt = "wav";
7592 ast_copy_string(vmfmts, fmt, sizeof(vmfmts));
7594 skipms = 3000;
7595 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
7596 if (sscanf(s, "%d", &x) == 1) {
7597 maxgreet = x;
7598 } else {
7599 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
7603 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
7604 if (sscanf(s, "%d", &x) == 1) {
7605 skipms = x;
7606 } else {
7607 ast_log(LOG_WARNING, "Invalid skipms value\n");
7611 maxlogins = 3;
7612 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
7613 if (sscanf(s, "%d", &x) == 1) {
7614 maxlogins = x;
7615 } else {
7616 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
7620 /* Force new user to record name ? */
7621 if (!(astforcename = ast_variable_retrieve(cfg, "general", "forcename")))
7622 astforcename = "no";
7623 ast_set2_flag((&globalflags), ast_true(astforcename), VM_FORCENAME);
7625 /* Force new user to record greetings ? */
7626 if (!(astforcegreet = ast_variable_retrieve(cfg, "general", "forcegreetings")))
7627 astforcegreet = "no";
7628 ast_set2_flag((&globalflags), ast_true(astforcegreet), VM_FORCEGREET);
7630 if ((s = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))){
7631 if (option_debug > 2)
7632 ast_log(LOG_DEBUG,"VM_CID Internal context string: %s\n",s);
7633 stringp = ast_strdupa(s);
7634 for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
7635 if (!ast_strlen_zero(stringp)) {
7636 q = strsep(&stringp,",");
7637 while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
7638 q++;
7639 ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
7640 if (option_debug > 2)
7641 ast_log(LOG_DEBUG,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
7642 } else {
7643 cidinternalcontexts[x][0] = '\0';
7647 if (!(astreview = ast_variable_retrieve(cfg, "general", "review"))){
7648 if (option_debug)
7649 ast_log(LOG_DEBUG,"VM Review Option disabled globally\n");
7650 astreview = "no";
7652 ast_set2_flag((&globalflags), ast_true(astreview), VM_REVIEW);
7654 /*Temperary greeting reminder */
7655 if (!(asttempgreetwarn = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
7656 if (option_debug)
7657 ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option disabled globally\n");
7658 asttempgreetwarn = "no";
7659 } else {
7660 if (option_debug)
7661 ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option enabled globally\n");
7663 ast_set2_flag((&globalflags), ast_true(asttempgreetwarn), VM_TEMPGREETWARN);
7665 if (!(astcallop = ast_variable_retrieve(cfg, "general", "operator"))){
7666 if (option_debug)
7667 ast_log(LOG_DEBUG,"VM Operator break disabled globally\n");
7668 astcallop = "no";
7670 ast_set2_flag((&globalflags), ast_true(astcallop), VM_OPERATOR);
7672 if (!(astsaycid = ast_variable_retrieve(cfg, "general", "saycid"))) {
7673 if (option_debug)
7674 ast_log(LOG_DEBUG,"VM CID Info before msg disabled globally\n");
7675 astsaycid = "no";
7677 ast_set2_flag((&globalflags), ast_true(astsaycid), VM_SAYCID);
7679 if (!(send_voicemail = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
7680 if (option_debug)
7681 ast_log(LOG_DEBUG,"Send Voicemail msg disabled globally\n");
7682 send_voicemail = "no";
7684 ast_set2_flag((&globalflags), ast_true(send_voicemail), VM_SVMAIL);
7686 if (!(asthearenv = ast_variable_retrieve(cfg, "general", "envelope"))) {
7687 if (option_debug)
7688 ast_log(LOG_DEBUG,"ENVELOPE before msg enabled globally\n");
7689 asthearenv = "yes";
7691 ast_set2_flag((&globalflags), ast_true(asthearenv), VM_ENVELOPE);
7693 if (!(astsaydurationinfo = ast_variable_retrieve(cfg, "general", "sayduration"))) {
7694 if (option_debug)
7695 ast_log(LOG_DEBUG,"Duration info before msg enabled globally\n");
7696 astsaydurationinfo = "yes";
7698 ast_set2_flag((&globalflags), ast_true(astsaydurationinfo), VM_SAYDURATION);
7700 saydurationminfo = 2;
7701 if ((astsaydurationminfo = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
7702 if (sscanf(astsaydurationminfo, "%d", &x) == 1) {
7703 saydurationminfo = x;
7704 } else {
7705 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
7709 if (!(astskipcmd = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
7710 if (option_debug)
7711 ast_log(LOG_DEBUG,"We are not going to skip to the next msg after save/delete\n");
7712 astskipcmd = "no";
7714 ast_set2_flag((&globalflags), ast_true(astskipcmd), VM_SKIPAFTERCMD);
7716 if ((dialoutcxt = ast_variable_retrieve(cfg, "general", "dialout"))) {
7717 ast_copy_string(dialcontext, dialoutcxt, sizeof(dialcontext));
7718 if (option_debug)
7719 ast_log(LOG_DEBUG, "found dialout context: %s\n", dialcontext);
7720 } else {
7721 dialcontext[0] = '\0';
7724 if ((callbackcxt = ast_variable_retrieve(cfg, "general", "callback"))) {
7725 ast_copy_string(callcontext, callbackcxt, sizeof(callcontext));
7726 if (option_debug)
7727 ast_log(LOG_DEBUG, "found callback context: %s\n", callcontext);
7728 } else {
7729 callcontext[0] = '\0';
7732 if ((exitcxt = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
7733 ast_copy_string(exitcontext, exitcxt, sizeof(exitcontext));
7734 if (option_debug)
7735 ast_log(LOG_DEBUG, "found operator context: %s\n", exitcontext);
7736 } else {
7737 exitcontext[0] = '\0';
7740 if (!(astdirfwd = ast_variable_retrieve(cfg, "general", "usedirectory")))
7741 astdirfwd = "no";
7742 ast_set2_flag((&globalflags), ast_true(astdirfwd), VM_DIRECFORWARD);
7743 if ((ucfg = ast_config_load("users.conf"))) {
7744 for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
7745 if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
7746 continue;
7747 if ((cur = find_or_create(userscontext, cat))) {
7748 populate_defaults(cur);
7749 apply_options_full(cur, ast_variable_browse(ucfg, cat));
7750 ast_copy_string(cur->context, userscontext, sizeof(cur->context));
7753 ast_config_destroy(ucfg);
7755 cat = ast_category_browse(cfg, NULL);
7756 while (cat) {
7757 if (strcasecmp(cat, "general")) {
7758 var = ast_variable_browse(cfg, cat);
7759 if (strcasecmp(cat, "zonemessages")) {
7760 /* Process mailboxes in this context */
7761 while (var) {
7762 append_mailbox(cat, var->name, var->value);
7763 var = var->next;
7765 } else {
7766 /* Timezones in this context */
7767 while (var) {
7768 struct vm_zone *z;
7769 if ((z = ast_malloc(sizeof(*z)))) {
7770 char *msg_format, *timezone;
7771 msg_format = ast_strdupa(var->value);
7772 timezone = strsep(&msg_format, "|");
7773 if (msg_format) {
7774 ast_copy_string(z->name, var->name, sizeof(z->name));
7775 ast_copy_string(z->timezone, timezone, sizeof(z->timezone));
7776 ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
7777 AST_LIST_LOCK(&zones);
7778 AST_LIST_INSERT_HEAD(&zones, z, list);
7779 AST_LIST_UNLOCK(&zones);
7780 } else {
7781 ast_log(LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
7782 free(z);
7784 } else {
7785 free(z);
7786 AST_LIST_UNLOCK(&users);
7787 ast_config_destroy(cfg);
7788 return -1;
7790 var = var->next;
7794 cat = ast_category_browse(cfg, cat);
7796 memset(fromstring,0,sizeof(fromstring));
7797 memset(pagerfromstring,0,sizeof(pagerfromstring));
7798 memset(emailtitle,0,sizeof(emailtitle));
7799 strcpy(charset, "ISO-8859-1");
7800 if (emailbody) {
7801 free(emailbody);
7802 emailbody = NULL;
7804 if (emailsubject) {
7805 free(emailsubject);
7806 emailsubject = NULL;
7808 if (pagerbody) {
7809 free(pagerbody);
7810 pagerbody = NULL;
7812 if (pagersubject) {
7813 free(pagersubject);
7814 pagersubject = NULL;
7816 if ((s = ast_variable_retrieve(cfg, "general", "pbxskip")))
7817 ast_set2_flag((&globalflags), ast_true(s), VM_PBXSKIP);
7818 if ((s = ast_variable_retrieve(cfg, "general", "fromstring")))
7819 ast_copy_string(fromstring,s,sizeof(fromstring));
7820 if ((s = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
7821 ast_copy_string(pagerfromstring,s,sizeof(pagerfromstring));
7822 if ((s = ast_variable_retrieve(cfg, "general", "charset")))
7823 ast_copy_string(charset,s,sizeof(charset));
7824 if ((s = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
7825 sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
7826 for (x = 0; x < 4; x++) {
7827 memcpy(&adsifdn[x], &tmpadsi[x], 1);
7830 if ((s = ast_variable_retrieve(cfg, "general", "adsisec"))) {
7831 sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
7832 for (x = 0; x < 4; x++) {
7833 memcpy(&adsisec[x], &tmpadsi[x], 1);
7836 if ((s = ast_variable_retrieve(cfg, "general", "adsiver")))
7837 if (atoi(s)) {
7838 adsiver = atoi(s);
7840 if ((s = ast_variable_retrieve(cfg, "general", "emailtitle"))) {
7841 ast_log(LOG_NOTICE, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
7842 ast_copy_string(emailtitle,s,sizeof(emailtitle));
7844 if ((s = ast_variable_retrieve(cfg, "general", "emailsubject")))
7845 emailsubject = ast_strdup(s);
7846 if ((s = ast_variable_retrieve(cfg, "general", "emailbody"))) {
7847 char *tmpread, *tmpwrite;
7848 emailbody = ast_strdup(s);
7850 /* substitute strings \t and \n into the appropriate characters */
7851 tmpread = tmpwrite = emailbody;
7852 while ((tmpwrite = strchr(tmpread,'\\'))) {
7853 switch (tmpwrite[1]) {
7854 case 'r':
7855 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7856 *tmpwrite = '\r';
7857 break;
7858 case 'n':
7859 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7860 *tmpwrite = '\n';
7861 break;
7862 case 't':
7863 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7864 *tmpwrite = '\t';
7865 break;
7866 default:
7867 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
7869 tmpread = tmpwrite + 1;
7872 if ((s = ast_variable_retrieve(cfg, "general", "pagersubject")))
7873 pagersubject = ast_strdup(s);
7874 if ((s = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
7875 char *tmpread, *tmpwrite;
7876 pagerbody = ast_strdup(s);
7878 /* substitute strings \t and \n into the appropriate characters */
7879 tmpread = tmpwrite = pagerbody;
7880 while ((tmpwrite = strchr(tmpread, '\\'))) {
7881 switch (tmpwrite[1]) {
7882 case 'r':
7883 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7884 *tmpwrite = '\r';
7885 break;
7886 case 'n':
7887 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7888 *tmpwrite = '\n';
7889 break;
7890 case 't':
7891 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7892 *tmpwrite = '\t';
7893 break;
7894 default:
7895 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
7897 tmpread = tmpwrite + 1;
7900 AST_LIST_UNLOCK(&users);
7901 ast_config_destroy(cfg);
7902 return 0;
7903 } else {
7904 AST_LIST_UNLOCK(&users);
7905 ast_log(LOG_WARNING, "Failed to load configuration file.\n");
7906 return 0;
7910 static int reload(void)
7912 return(load_config());
7915 static int unload_module(void)
7917 int res;
7919 res = ast_unregister_application(app);
7920 res |= ast_unregister_application(app2);
7921 res |= ast_unregister_application(app3);
7922 res |= ast_unregister_application(app4);
7923 ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
7924 ast_uninstall_vm_functions();
7926 ast_module_user_hangup_all();
7928 return res;
7931 static int load_module(void)
7933 int res;
7934 char *adsi_loaded = ast_module_helper("", "res_adsi.so", 0, 0, 0, 0);
7935 free(adsi_loaded);
7936 if (!adsi_loaded) {
7937 /* If embedded, res_adsi may be known as "res_adsi" not "res_adsi.so" */
7938 adsi_loaded = ast_module_helper("", "res_adsi", 0, 0, 0, 0);
7939 ast_free(adsi_loaded);
7940 if (!adsi_loaded) {
7941 ast_log(LOG_ERROR, "app_voicemail.so depends upon res_adsi.so\n");
7942 return AST_MODULE_LOAD_DECLINE;
7946 my_umask = umask(0);
7947 umask(my_umask);
7948 res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
7949 res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
7950 res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
7951 res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
7952 if (res)
7953 return(res);
7955 if ((res=load_config())) {
7956 return(res);
7959 ast_cli_register_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
7961 /* compute the location of the voicemail spool directory */
7962 snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
7964 ast_install_vm_functions(has_voicemail, inboxcount, messagecount);
7966 return res;
7969 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
7971 int cmd = 0;
7972 char destination[80] = "";
7973 int retries = 0;
7975 if (!num) {
7976 if (option_verbose > 2)
7977 ast_verbose( VERBOSE_PREFIX_3 "Destination number will be entered manually\n");
7978 while (retries < 3 && cmd != 't') {
7979 destination[1] = '\0';
7980 destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
7981 if (!cmd)
7982 destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
7983 if (!cmd)
7984 destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
7985 if (!cmd) {
7986 cmd = ast_waitfordigit(chan, 6000);
7987 if (cmd)
7988 destination[0] = cmd;
7990 if (!cmd) {
7991 retries++;
7992 } else {
7994 if (cmd < 0)
7995 return 0;
7996 if (cmd == '*') {
7997 if (option_verbose > 2)
7998 ast_verbose( VERBOSE_PREFIX_3 "User hit '*' to cancel outgoing call\n");
7999 return 0;
8001 if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0)
8002 retries++;
8003 else
8004 cmd = 't';
8007 if (retries >= 3) {
8008 return 0;
8011 } else {
8012 if (option_verbose > 2)
8013 ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
8014 ast_copy_string(destination, num, sizeof(destination));
8017 if (!ast_strlen_zero(destination)) {
8018 if (destination[strlen(destination) -1 ] == '*')
8019 return 0;
8020 if (option_verbose > 2)
8021 ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
8022 ast_copy_string(chan->exten, destination, sizeof(chan->exten));
8023 ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
8024 chan->priority = 0;
8025 return 9;
8027 return 0;
8030 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain)
8032 int res = 0;
8033 #ifdef IMAP_STORAGE
8034 char origtimeS[256],cidS[256],contextS[256];
8035 char *header_content,*temp;
8036 #endif
8037 char filename[PATH_MAX];
8038 struct ast_config *msg_cfg = NULL;
8039 const char *origtime, *context;
8040 char *cid, *name, *num;
8041 int retries = 0;
8043 vms->starting = 0;
8044 #ifdef IMAP_STORAGE
8045 /* START HERE */
8046 /* get the message info!! */
8047 if (option_debug > 2)
8048 ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
8049 if (vms->msgArray[vms->curmsg] == 0) {
8050 ast_log (LOG_WARNING,"Trying to access unknown message\n");
8051 return -1;
8054 /* This will only work for new messages... */
8055 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
8056 /* empty string means no valid header */
8057 if (ast_strlen_zero(header_content)) {
8058 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
8059 return -1;
8062 /* Get info from headers!! */
8063 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:");
8065 if (temp)
8066 ast_copy_string(cidS,temp, sizeof(cidS));
8067 else
8068 cidS[0] = '\0';
8070 cid = &cidS[0];
8071 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Context:");
8073 if (temp)
8074 ast_copy_string(contextS,temp, sizeof(contextS));
8075 else
8076 contextS[0] = '\0';
8078 context = &contextS[0];
8079 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:");
8081 if (temp)
8082 ast_copy_string(origtimeS,temp, sizeof(origtimeS));
8083 else
8084 origtimeS[0] = '\0';
8086 origtime = &origtimeS[0];
8088 ast_copy_string(filename, "IMAP_STORAGE", sizeof(filename));
8089 #else
8090 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
8092 /* Retrieve info from VM attribute file */
8094 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
8095 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
8096 RETRIEVE(vms->curdir, vms->curmsg);
8097 msg_cfg = ast_config_load(filename);
8098 DISPOSE(vms->curdir, vms->curmsg);
8099 if (!msg_cfg) {
8100 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
8101 return 0;
8104 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
8105 ast_config_destroy(msg_cfg);
8106 return 0;
8109 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
8111 context = ast_variable_retrieve(msg_cfg, "message", "context");
8112 if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
8113 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
8114 #endif
8115 switch (option) {
8116 case 3:
8117 if (!res)
8118 res = play_message_datetime(chan, vmu, origtime, filename);
8119 if (!res)
8120 res = play_message_callerid(chan, vms, cid, context, 0);
8122 res = 't';
8123 break;
8125 case 2: /* Call back */
8127 if (ast_strlen_zero(cid))
8128 break;
8130 ast_callerid_parse(cid, &name, &num);
8131 while ((res > -1) && (res != 't')) {
8132 switch (res) {
8133 case '1':
8134 if (num) {
8135 /* Dial the CID number */
8136 res = dialout(chan, vmu, num, vmu->callback);
8137 if (res) {
8138 ast_config_destroy(msg_cfg);
8139 return 9;
8141 } else {
8142 res = '2';
8144 break;
8146 case '2':
8147 /* Want to enter a different number, can only do this if there's a dialout context for this user */
8148 if (!ast_strlen_zero(vmu->dialout)) {
8149 res = dialout(chan, vmu, NULL, vmu->dialout);
8150 if (res) {
8151 ast_config_destroy(msg_cfg);
8152 return 9;
8154 } else {
8155 if (option_verbose > 2)
8156 ast_verbose( VERBOSE_PREFIX_3 "Caller can not specify callback number - no dialout context available\n");
8157 res = ast_play_and_wait(chan, "vm-sorry");
8159 ast_config_destroy(msg_cfg);
8160 return res;
8161 case '*':
8162 res = 't';
8163 break;
8164 case '3':
8165 case '4':
8166 case '5':
8167 case '6':
8168 case '7':
8169 case '8':
8170 case '9':
8171 case '0':
8173 res = ast_play_and_wait(chan, "vm-sorry");
8174 retries++;
8175 break;
8176 default:
8177 if (num) {
8178 if (option_verbose > 2)
8179 ast_verbose( VERBOSE_PREFIX_3 "Confirm CID number '%s' is number to use for callback\n", num);
8180 res = ast_play_and_wait(chan, "vm-num-i-have");
8181 if (!res)
8182 res = play_message_callerid(chan, vms, num, vmu->context, 1);
8183 if (!res)
8184 res = ast_play_and_wait(chan, "vm-tocallnum");
8185 /* Only prompt for a caller-specified number if there is a dialout context specified */
8186 if (!ast_strlen_zero(vmu->dialout)) {
8187 if (!res)
8188 res = ast_play_and_wait(chan, "vm-calldiffnum");
8190 } else {
8191 res = ast_play_and_wait(chan, "vm-nonumber");
8192 if (!ast_strlen_zero(vmu->dialout)) {
8193 if (!res)
8194 res = ast_play_and_wait(chan, "vm-toenternumber");
8197 if (!res)
8198 res = ast_play_and_wait(chan, "vm-star-cancel");
8199 if (!res)
8200 res = ast_waitfordigit(chan, 6000);
8201 if (!res) {
8202 retries++;
8203 if (retries > 3)
8204 res = 't';
8206 break;
8209 if (res == 't')
8210 res = 0;
8211 else if (res == '*')
8212 res = -1;
8214 break;
8216 case 1: /* Reply */
8217 /* Send reply directly to sender */
8218 if (ast_strlen_zero(cid))
8219 break;
8221 ast_callerid_parse(cid, &name, &num);
8222 if (!num) {
8223 if (option_verbose > 2)
8224 ast_verbose(VERBOSE_PREFIX_3 "No CID number available, no reply sent\n");
8225 if (!res)
8226 res = ast_play_and_wait(chan, "vm-nonumber");
8227 ast_config_destroy(msg_cfg);
8228 return res;
8229 } else {
8230 struct ast_vm_user vmu2;
8231 if (find_user(&vmu2, vmu->context, num)) {
8232 struct leave_vm_options leave_options;
8233 char mailbox[AST_MAX_EXTENSION * 2 + 2];
8234 snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
8236 if (option_verbose > 2)
8237 ast_verbose(VERBOSE_PREFIX_3 "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
8239 memset(&leave_options, 0, sizeof(leave_options));
8240 leave_options.record_gain = record_gain;
8241 res = leave_voicemail(chan, mailbox, &leave_options);
8242 if (!res)
8243 res = 't';
8244 ast_config_destroy(msg_cfg);
8245 return res;
8246 } else {
8247 /* Sender has no mailbox, can't reply */
8248 if (option_verbose > 2)
8249 ast_verbose( VERBOSE_PREFIX_3 "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
8250 ast_play_and_wait(chan, "vm-nobox");
8251 res = 't';
8252 ast_config_destroy(msg_cfg);
8253 return res;
8256 res = 0;
8258 break;
8261 #ifndef IMAP_STORAGE
8262 ast_config_destroy(msg_cfg);
8264 if (!res) {
8265 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
8266 vms->heard[msg] = 1;
8267 res = wait_file(chan, vms, vms->fn);
8269 #endif
8270 return res;
8273 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
8274 int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
8275 signed char record_gain, struct vm_state *vms)
8277 /* Record message & let caller review or re-record it, or set options if applicable */
8278 int res = 0;
8279 int cmd = 0;
8280 int max_attempts = 3;
8281 int attempts = 0;
8282 int recorded = 0;
8283 int message_exists = 0;
8284 signed char zero_gain = 0;
8285 char tempfile[PATH_MAX];
8286 char *acceptdtmf = "#";
8287 char *canceldtmf = "";
8289 /* Note that urgent and private are for flagging messages as such in the future */
8291 /* barf if no pointer passed to store duration in */
8292 if (duration == NULL) {
8293 ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
8294 return -1;
8297 if (!outsidecaller)
8298 snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
8299 else
8300 ast_copy_string(tempfile, recordfile, sizeof(tempfile));
8302 cmd = '3'; /* Want to start by recording */
8304 while ((cmd >= 0) && (cmd != 't')) {
8305 switch (cmd) {
8306 case '1':
8307 if (!message_exists) {
8308 /* In this case, 1 is to record a message */
8309 cmd = '3';
8310 break;
8311 } else {
8312 /* Otherwise 1 is to save the existing message */
8313 if (option_verbose > 2)
8314 ast_verbose(VERBOSE_PREFIX_3 "Saving message as is\n");
8315 if (!outsidecaller)
8316 ast_filerename(tempfile, recordfile, NULL);
8317 ast_stream_and_wait(chan, "vm-msgsaved", chan->language, "");
8318 if (!outsidecaller) {
8319 STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms);
8320 DISPOSE(recordfile, -1);
8322 cmd = 't';
8323 return res;
8325 case '2':
8326 /* Review */
8327 if (option_verbose > 2)
8328 ast_verbose(VERBOSE_PREFIX_3 "Reviewing the message\n");
8329 cmd = ast_stream_and_wait(chan, tempfile, chan->language, AST_DIGIT_ANY);
8330 break;
8331 case '3':
8332 message_exists = 0;
8333 /* Record */
8334 if (recorded == 1) {
8335 if (option_verbose > 2)
8336 ast_verbose(VERBOSE_PREFIX_3 "Re-recording the message\n");
8337 } else {
8338 if (option_verbose > 2)
8339 ast_verbose(VERBOSE_PREFIX_3 "Recording the message\n");
8341 if (recorded && outsidecaller) {
8342 cmd = ast_play_and_wait(chan, INTRO);
8343 cmd = ast_play_and_wait(chan, "beep");
8345 recorded = 1;
8346 /* After an attempt has been made to record message, we have to take care of INTRO and beep for incoming messages, but not for greetings */
8347 if (record_gain)
8348 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
8349 if (ast_test_flag(vmu, VM_OPERATOR))
8350 canceldtmf = "0";
8351 cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
8352 if (record_gain)
8353 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
8354 if (cmd == -1) {
8355 /* User has hung up, no options to give */
8356 if (!outsidecaller) {
8357 /* user was recording a greeting and they hung up, so let's delete the recording. */
8358 ast_filedelete(tempfile, NULL);
8360 return cmd;
8362 if (cmd == '0') {
8363 break;
8364 } else if (cmd == '*') {
8365 break;
8367 #if 0
8368 else if (vmu->review && (*duration < 5)) {
8369 /* Message is too short */
8370 if (option_verbose > 2)
8371 ast_verbose(VERBOSE_PREFIX_3 "Message too short\n");
8372 cmd = ast_play_and_wait(chan, "vm-tooshort");
8373 cmd = ast_filedelete(tempfile, NULL);
8374 break;
8376 else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
8377 /* Message is all silence */
8378 if (option_verbose > 2)
8379 ast_verbose(VERBOSE_PREFIX_3 "Nothing recorded\n");
8380 cmd = ast_filedelete(tempfile, NULL);
8381 cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
8382 if (!cmd)
8383 cmd = ast_play_and_wait(chan, "vm-speakup");
8384 break;
8386 #endif
8387 else {
8388 /* If all is well, a message exists */
8389 message_exists = 1;
8390 cmd = 0;
8392 break;
8393 case '4':
8394 case '5':
8395 case '6':
8396 case '7':
8397 case '8':
8398 case '9':
8399 case '*':
8400 case '#':
8401 cmd = ast_play_and_wait(chan, "vm-sorry");
8402 break;
8403 #if 0
8404 /* XXX Commented out for the moment because of the dangers of deleting
8405 a message while recording (can put the message numbers out of sync) */
8406 case '*':
8407 /* Cancel recording, delete message, offer to take another message*/
8408 cmd = ast_play_and_wait(chan, "vm-deleted");
8409 cmd = ast_filedelete(tempfile, NULL);
8410 if (outsidecaller) {
8411 res = vm_exec(chan, NULL);
8412 return res;
8414 else
8415 return 1;
8416 #endif
8417 case '0':
8418 if (!ast_test_flag(vmu, VM_OPERATOR)) {
8419 cmd = ast_play_and_wait(chan, "vm-sorry");
8420 break;
8422 if (message_exists || recorded) {
8423 cmd = ast_play_and_wait(chan, "vm-saveoper");
8424 if (!cmd)
8425 cmd = ast_waitfordigit(chan, 3000);
8426 if (cmd == '1') {
8427 ast_play_and_wait(chan, "vm-msgsaved");
8428 cmd = '0';
8429 } else {
8430 ast_play_and_wait(chan, "vm-deleted");
8431 DELETE(recordfile, -1, recordfile);
8432 cmd = '0';
8435 return cmd;
8436 default:
8437 /* If the caller is an ouside caller, and the review option is enabled,
8438 allow them to review the message, but let the owner of the box review
8439 their OGM's */
8440 if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
8441 return cmd;
8442 if (message_exists) {
8443 cmd = ast_play_and_wait(chan, "vm-review");
8445 else {
8446 cmd = ast_play_and_wait(chan, "vm-torerecord");
8447 if (!cmd)
8448 cmd = ast_waitfordigit(chan, 600);
8451 if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
8452 cmd = ast_play_and_wait(chan, "vm-reachoper");
8453 if (!cmd)
8454 cmd = ast_waitfordigit(chan, 600);
8456 #if 0
8457 if (!cmd)
8458 cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
8459 #endif
8460 if (!cmd)
8461 cmd = ast_waitfordigit(chan, 6000);
8462 if (!cmd) {
8463 attempts++;
8465 if (attempts > max_attempts) {
8466 cmd = 't';
8470 if (outsidecaller)
8471 ast_play_and_wait(chan, "vm-goodbye");
8472 if (cmd == 't')
8473 cmd = 0;
8474 return cmd;
8477 #ifdef IMAP_STORAGE
8479 static void write_file(char *filename, char *buffer, unsigned long len)
8481 FILE *output;
8483 output = fopen (filename, "w");
8484 fwrite (buffer, len, 1, output);
8485 fclose (output);
8488 void mm_searched(MAILSTREAM *stream, unsigned long number)
8490 struct vm_state *vms;
8491 char *mailbox;
8492 char *user;
8493 mailbox = stream->mailbox;
8494 user = get_user_by_mailbox(mailbox);
8495 vms = get_vm_state_by_imapuser(user,2);
8496 if (vms) {
8497 if (option_debug > 2)
8498 ast_log (LOG_DEBUG, "saving mailbox message number %lu as message %d. Interactive set to %d\n",number,vms->vmArrayIndex,vms->interactive);
8499 vms->msgArray[vms->vmArrayIndex++] = number;
8500 } else {
8501 ast_log (LOG_ERROR, "No state found.\n");
8506 #if 0 /*No need for this. */
8507 /* MM status report
8508 * Accepts: MAIL stream
8510 static void status(MAILSTREAM *stream)
8512 unsigned long i;
8513 char *s, date[MAILTMPLEN];
8514 THREADER *thr;
8515 AUTHENTICATOR *auth;
8516 rfc822_date (date);
8517 ast_log (LOG_NOTICE,"%s\n",date);
8518 if (stream) {
8519 if (stream->mailbox)
8520 ast_log (LOG_NOTICE," %s mailbox: %s, %lu messages, %lu recent\n",
8521 stream->dtb->name, stream->mailbox, stream->nmsgs,stream->recent);
8522 else
8523 ast_log (LOG_NOTICE,"No mailbox is open on this stream\n");
8524 if (stream->user_flags[0]) {
8525 ast_log (LOG_NOTICE,"Keywords: %s\n", stream->user_flags[0]);
8526 for (i = 1; i < NUSERFLAGS && stream->user_flags[i]; ++i)
8527 ast_log (LOG_NOTICE," %s\n", stream->user_flags[i]);
8529 if (!strcmp (stream->dtb->name, "imap")) {
8530 if (LEVELIMAP4rev1 (stream))
8531 s = "IMAP4rev1 (RFC 3501)";
8532 else if (LEVEL1730 (stream))
8533 s = "IMAP4 (RFC 1730)";
8534 else if (LEVELIMAP2bis (stream))
8535 s = "IMAP2bis";
8536 else if (LEVEL1176 (stream))
8537 s = "IMAP2 (RFC 1176)";
8538 else
8539 s = "IMAP2 (RFC 1064)";
8540 ast_log (LOG_NOTICE,"%s server %s\n", s, imap_host (stream));
8541 if (LEVELIMAP4 (stream)) {
8542 if ((i = (imap_cap(stream)->auth))) {
8543 s = "";
8544 ast_log (LOG_NOTICE,"Mutually-supported SASL mechanisms:\n");
8545 while ((auth = mail_lookup_auth (find_rightmost_bit (&i) + 1))) {
8546 ast_log (LOG_NOTICE," %s\n", auth->name);
8547 if (!strcmp (auth->name, "PLAIN"))
8548 s = "\n [LOGIN will not be listed here if PLAIN is supported]\n";
8550 ast_log (LOG_NOTICE,s);
8552 ast_log (LOG_NOTICE,"Supported standard extensions:\n");
8553 if (LEVELACL (stream))
8554 ast_log (LOG_NOTICE," Access Control lists (RFC 2086)\n");
8555 if (LEVELQUOTA (stream))
8556 ast_log (LOG_NOTICE," Quotas (RFC 2087)\n");
8557 if (LEVELLITERALPLUS (stream))
8558 ast_log (LOG_NOTICE," Non-synchronizing literals (RFC 2088)\n");
8559 if (LEVELIDLE (stream))
8560 ast_log (LOG_NOTICE," IDLE unsolicited update (RFC 2177)\n");
8561 if (LEVELMBX_REF (stream))
8562 ast_log (LOG_NOTICE," Mailbox referrals (RFC 2193)\n");
8563 if (LEVELLOG_REF (stream))
8564 ast_log (LOG_NOTICE," Login referrals (RFC 2221)\n");
8565 if (LEVELANONYMOUS (stream))
8566 ast_log (LOG_NOTICE," Anonymous access (RFC 2245)\n");
8567 if (LEVELNAMESPACE (stream))
8568 ast_log (LOG_NOTICE," Multiple namespaces (RFC 2342)\n");
8569 if (LEVELUIDPLUS (stream))
8570 ast_log (LOG_NOTICE," Extended UID behavior (RFC 2359)\n");
8571 if (LEVELSTARTTLS (stream))
8572 ast_log (LOG_NOTICE," Transport Layer Security (RFC 2595)\n");
8573 if (LEVELLOGINDISABLED (stream))
8574 ast_log (LOG_NOTICE," LOGIN command disabled (RFC 2595)\n");
8575 if (LEVELID (stream))
8576 ast_log (LOG_NOTICE," Implementation identity negotiation (RFC 2971)\n");
8577 if (LEVELCHILDREN (stream))
8578 ast_log (LOG_NOTICE," LIST children announcement (RFC 3348)\n");
8579 if (LEVELMULTIAPPEND (stream))
8580 ast_log (LOG_NOTICE," Atomic multiple APPEND (RFC 3502)\n");
8581 if (LEVELBINARY (stream))
8582 ast_log (LOG_NOTICE," Binary body content (RFC 3516)\n");
8583 ast_log (LOG_NOTICE,"Supported draft extensions:\n");
8584 if (LEVELUNSELECT (stream))
8585 ast_log (LOG_NOTICE," Mailbox unselect\n");
8586 if (LEVELSASLIR (stream))
8587 ast_log (LOG_NOTICE," SASL initial client response\n");
8588 if (LEVELSORT (stream))
8589 ast_log (LOG_NOTICE," Server-based sorting\n");
8590 if (LEVELTHREAD (stream)) {
8591 ast_log (LOG_NOTICE," Server-based threading:\n");
8592 for (thr = imap_cap(stream)->threader; thr; thr = thr->next)
8593 ast_log (LOG_NOTICE," %s\n", thr->name);
8595 if (LEVELSCAN (stream))
8596 ast_log (LOG_NOTICE," Mailbox text scan\n");
8597 if ((i = imap_cap(stream)->extlevel)) {
8598 ast_log (LOG_NOTICE,"Supported BODYSTRUCTURE extensions:\n");
8599 switch (i) {
8600 case BODYEXTLOC:
8601 ast_log (LOG_NOTICE," location\n");
8602 case BODYEXTLANG:
8603 ast_log (LOG_NOTICE," language\n");
8604 case BODYEXTDSP:
8605 ast_log (LOG_NOTICE," disposition\n");
8606 case BODYEXTMD5:
8607 ast_log (LOG_NOTICE," MD5\n");
8610 }else
8611 ast_log (LOG_NOTICE,"\n");
8615 #endif
8617 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
8619 struct ast_variable *var;
8620 struct ast_vm_user *vmu;
8622 vmu = ast_calloc(1, sizeof *vmu);
8623 if (!vmu)
8624 return NULL;
8625 ast_set_flag(vmu, VM_ALLOCED);
8626 populate_defaults(vmu);
8628 var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
8629 if (var) {
8630 apply_options_full(vmu, var);
8631 ast_variables_destroy(var);
8632 return vmu;
8633 } else {
8634 free(vmu);
8635 return NULL;
8639 /* Interfaces to C-client */
8641 void mm_exists(MAILSTREAM * stream, unsigned long number)
8643 /* mail_ping will callback here if new mail! */
8644 if (option_debug > 3)
8645 ast_log (LOG_DEBUG, "Entering EXISTS callback for message %ld\n", number);
8646 if (number == 0) return;
8647 set_update(stream);
8651 void mm_expunged(MAILSTREAM * stream, unsigned long number)
8653 /* mail_ping will callback here if expunged mail! */
8654 if (option_debug > 3)
8655 ast_log (LOG_DEBUG, "Entering EXPUNGE callback for message %ld\n", number);
8656 if (number == 0) return;
8657 set_update(stream);
8661 void mm_flags(MAILSTREAM * stream, unsigned long number)
8663 /* mail_ping will callback here if read mail! */
8664 if (option_debug > 3)
8665 ast_log (LOG_DEBUG, "Entering FLAGS callback for message %ld\n", number);
8666 if (number == 0) return;
8667 set_update(stream);
8671 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
8673 mm_log (string, errflg);
8677 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
8679 if (delimiter == '\0') {
8680 delimiter = delim;
8682 if (option_debug > 4) {
8683 ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
8684 if (attributes & LATT_NOINFERIORS)
8685 ast_log(LOG_DEBUG, "no inferiors\n");
8686 if (attributes & LATT_NOSELECT)
8687 ast_log(LOG_DEBUG, "no select\n");
8688 if (attributes & LATT_MARKED)
8689 ast_log(LOG_DEBUG, "marked\n");
8690 if (attributes & LATT_UNMARKED)
8691 ast_log(LOG_DEBUG, "unmarked\n");
8696 void mm_lsub(MAILSTREAM * stream, int delimiter, char *mailbox, long attributes)
8698 if (option_debug > 4) {
8699 ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delimiter, mailbox);
8700 if (attributes & LATT_NOINFERIORS)
8701 ast_log(LOG_DEBUG, "no inferiors\n");
8702 if (attributes & LATT_NOSELECT)
8703 ast_log(LOG_DEBUG, "no select\n");
8704 if (attributes & LATT_MARKED)
8705 ast_log(LOG_DEBUG, "marked\n");
8706 if (attributes & LATT_UNMARKED)
8707 ast_log(LOG_DEBUG, "unmarked\n");
8712 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
8714 ast_log (LOG_NOTICE," Mailbox %s", mailbox);
8715 if (status->flags & SA_MESSAGES)
8716 ast_log (LOG_NOTICE,", %lu messages", status->messages);
8717 if (status->flags & SA_RECENT)
8718 ast_log (LOG_NOTICE,", %lu recent", status->recent);
8719 if (status->flags & SA_UNSEEN)
8720 ast_log (LOG_NOTICE,", %lu unseen", status->unseen);
8721 if (status->flags & SA_UIDVALIDITY)
8722 ast_log (LOG_NOTICE,", %lu UID validity", status->uidvalidity);
8723 if (status->flags & SA_UIDNEXT)
8724 ast_log (LOG_NOTICE,", %lu next UID", status->uidnext);
8725 ast_log (LOG_NOTICE,"\n");
8729 void mm_log(char *string, long errflg)
8731 switch ((short) errflg) {
8732 case NIL:
8733 if (option_debug)
8734 ast_log(LOG_DEBUG,"IMAP Info: %s\n", string);
8735 break;
8736 case PARSE:
8737 case WARN:
8738 ast_log (LOG_WARNING,"IMAP Warning: %s\n", string);
8739 break;
8740 case ERROR:
8741 ast_log (LOG_ERROR,"IMAP Error: %s\n", string);
8742 break;
8747 void mm_dlog(char *string)
8749 ast_log (LOG_NOTICE, "%s\n", string);
8753 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
8755 struct ast_vm_user *vmu;
8757 if (option_debug > 3)
8758 ast_log(LOG_DEBUG, "Entering callback mm_login\n");
8760 ast_copy_string(user, mb->user, MAILTMPLEN);
8762 /* We should only do this when necessary */
8763 if (!ast_strlen_zero(authpassword)) {
8764 ast_copy_string(pwd, authpassword, MAILTMPLEN);
8765 } else {
8766 AST_LIST_TRAVERSE(&users, vmu, list) {
8767 if (!strcasecmp(mb->user, vmu->imapuser)) {
8768 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
8769 break;
8772 if (!vmu) {
8773 if ((vmu = find_user_realtime_imapuser(mb->user))) {
8774 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
8775 free_user(vmu);
8782 void mm_critical(MAILSTREAM * stream)
8787 void mm_nocritical(MAILSTREAM * stream)
8792 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
8794 kill (getpid (), SIGSTOP);
8795 return NIL;
8799 void mm_fatal(char *string)
8801 ast_log(LOG_ERROR,"IMAP access FATAL error: %s\n", string);
8804 /* C-client callback to handle quota */
8805 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
8807 struct vm_state *vms;
8808 char *mailbox;
8809 char *user;
8810 unsigned long usage = 0;
8811 unsigned long limit = 0;
8813 while (pquota) {
8814 usage = pquota->usage;
8815 limit = pquota->limit;
8816 pquota = pquota->next;
8819 mailbox = stream->mailbox;
8820 user = get_user_by_mailbox(mailbox);
8821 vms = get_vm_state_by_imapuser(user,2);
8822 if (vms) {
8823 if (option_debug > 2)
8824 ast_log (LOG_DEBUG, "User %s usage is %lu, limit is %lu\n",user,usage,limit);
8825 vms->quota_usage = usage;
8826 vms->quota_limit = limit;
8827 } else {
8828 ast_log (LOG_ERROR, "No state found.\n");
8832 static char *get_header_by_tag(char *header, char *tag)
8834 char *start;
8835 int taglen;
8836 char *eol_pnt;
8838 if (!header || !tag)
8839 return NULL;
8841 taglen = strlen(tag) + 1;
8842 if (taglen < 1)
8843 return NULL;
8845 start = strstr(header, tag);
8846 if (!start)
8847 return NULL;
8849 ast_mutex_lock(&imaptemp_lock);
8850 ast_copy_string(imaptemp, start+taglen, sizeof(imaptemp));
8851 ast_mutex_unlock(&imaptemp_lock);
8852 if ((eol_pnt = strchr(imaptemp,'\r')) || (eol_pnt = strchr(imaptemp,'\n')))
8853 *eol_pnt = '\0';
8854 return imaptemp;
8857 static char *get_user_by_mailbox(char *mailbox)
8859 char *start, *quote;
8860 char *eol_pnt;
8862 if (!mailbox)
8863 return NULL;
8865 start = strstr(mailbox,"/user=");
8866 if (!start)
8867 return NULL;
8869 ast_mutex_lock(&imaptemp_lock);
8870 ast_copy_string(imaptemp, start+6, sizeof(imaptemp));
8871 ast_mutex_unlock(&imaptemp_lock);
8873 quote = strchr(imaptemp,'\"');
8874 if (!quote) { /* if username is not in quotes */
8875 eol_pnt = strchr(imaptemp,'/');
8876 if (!eol_pnt) {
8877 eol_pnt = strchr(imaptemp,'}');
8879 *eol_pnt = '\0';
8880 return imaptemp;
8881 } else {
8882 eol_pnt = strchr(imaptemp+1,'\"');
8883 *eol_pnt = '\0';
8884 return imaptemp+1;
8888 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
8890 struct vmstate *vlist = NULL;
8892 ast_mutex_lock(&vmstate_lock);
8893 vlist = vmstates;
8894 while (vlist) {
8895 if (vlist->vms) {
8896 if (vlist->vms->imapuser) {
8897 if (!strcmp(vlist->vms->imapuser,user)) {
8898 if (interactive == 2) {
8899 ast_mutex_unlock(&vmstate_lock);
8900 return vlist->vms;
8901 } else if (vlist->vms->interactive == interactive) {
8902 ast_mutex_unlock(&vmstate_lock);
8903 return vlist->vms;
8906 } else {
8907 if (option_debug > 2)
8908 ast_log(LOG_DEBUG, " error: imapuser is NULL for %s\n",user);
8910 } else {
8911 if (option_debug > 2)
8912 ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",user);
8914 vlist = vlist->next;
8916 ast_mutex_unlock(&vmstate_lock);
8917 if (option_debug > 2)
8918 ast_log(LOG_DEBUG, "%s not found in vmstates\n",user);
8919 return NULL;
8922 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive)
8924 struct vmstate *vlist = NULL;
8926 ast_mutex_lock(&vmstate_lock);
8927 vlist = vmstates;
8928 if (option_debug > 2)
8929 ast_log(LOG_DEBUG, "Mailbox set to %s\n",mailbox);
8930 while (vlist) {
8931 if (vlist->vms) {
8932 if (vlist->vms->username) {
8933 if (option_debug > 2)
8934 ast_log(LOG_DEBUG, " comparing mailbox %s (i=%d) to vmstate mailbox %s (i=%d)\n",mailbox,interactive,vlist->vms->username,vlist->vms->interactive);
8935 if (!strcmp(vlist->vms->username,mailbox) && vlist->vms->interactive == interactive) {
8936 if (option_debug > 2)
8937 ast_log(LOG_DEBUG, " Found it!\n");
8938 ast_mutex_unlock(&vmstate_lock);
8939 return vlist->vms;
8941 } else {
8942 if (option_debug > 2)
8943 ast_log(LOG_DEBUG, " error: username is NULL for %s\n",mailbox);
8945 } else {
8946 if (option_debug > 2)
8947 ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",mailbox);
8949 vlist = vlist->next;
8951 ast_mutex_unlock(&vmstate_lock);
8952 if (option_debug > 2)
8953 ast_log(LOG_DEBUG, "%s not found in vmstates\n",mailbox);
8954 return NULL;
8957 static void vmstate_insert(struct vm_state *vms)
8959 struct vmstate *v;
8960 struct vm_state *altvms;
8962 /* If interactive, it probably already exists, and we should
8963 use the one we already have since it is more up to date.
8964 We can compare the username to find the duplicate */
8965 if (vms->interactive == 1) {
8966 altvms = get_vm_state_by_mailbox(vms->username,0);
8967 if (altvms) {
8968 if (option_debug > 2)
8969 ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
8970 vms->newmessages = altvms->newmessages;
8971 vms->oldmessages = altvms->oldmessages;
8972 if (option_debug > 2)
8973 ast_log(LOG_DEBUG, "check_msgArray before memcpy\n");
8974 check_msgArray(vms);
8975 /* memcpy(vms->msgArray, altvms->msgArray, sizeof(long)*256); */
8976 copy_msgArray(vms, altvms);
8977 if (option_debug > 2)
8978 ast_log(LOG_DEBUG, "check_msgArray after memcpy\n");
8979 check_msgArray(vms);
8980 vms->vmArrayIndex = altvms->vmArrayIndex;
8981 vms->lastmsg = altvms->lastmsg;
8982 vms->curmsg = altvms->curmsg;
8983 /* get a pointer to the persistent store */
8984 vms->persist_vms = altvms;
8985 /* Reuse the mailstream? */
8986 vms->mailstream = altvms->mailstream;
8987 /* vms->mailstream = NIL; */
8991 v = (struct vmstate *)malloc(sizeof(struct vmstate));
8992 if (!v) {
8993 ast_log(LOG_ERROR, "Out of memory\n");
8995 if (option_debug > 2)
8996 ast_log(LOG_DEBUG, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
8997 ast_mutex_lock(&vmstate_lock);
8998 v->vms = vms;
8999 v->next = vmstates;
9000 vmstates = v;
9001 ast_mutex_unlock(&vmstate_lock);
9004 static void vmstate_delete(struct vm_state *vms)
9006 struct vmstate *vc, *vf = NULL, *vl = NULL;
9007 struct vm_state *altvms;
9009 /* If interactive, we should copy pertainent info
9010 back to the persistent state (to make update immediate) */
9011 if (vms->interactive == 1) {
9012 altvms = vms->persist_vms;
9013 if (altvms) {
9014 if (option_debug > 2)
9015 ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
9016 altvms->newmessages = vms->newmessages;
9017 altvms->oldmessages = vms->oldmessages;
9018 altvms->updated = 1;
9022 ast_mutex_lock(&vmstate_lock);
9023 vc = vmstates;
9024 if (option_debug > 2)
9025 ast_log(LOG_DEBUG, "Removing vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
9026 while (vc) {
9027 if (vc->vms == vms) {
9028 vf = vc;
9029 if (vl)
9030 vl->next = vc->next;
9031 else
9032 vmstates = vc->next;
9033 break;
9035 vl = vc;
9036 vc = vc->next;
9038 if (!vf) {
9039 ast_log(LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n",vms->imapuser,vms->username);
9040 } else {
9041 ast_mutex_destroy(&vf->vms->lock);
9042 free(vf);
9044 ast_mutex_unlock(&vmstate_lock);
9047 static void set_update(MAILSTREAM * stream)
9049 struct vm_state *vms;
9050 char *mailbox;
9051 char *user;
9053 mailbox = stream->mailbox;
9054 user = get_user_by_mailbox(mailbox);
9055 vms = get_vm_state_by_imapuser(user, 0);
9056 if (vms) {
9057 if (option_debug > 2)
9058 ast_log (LOG_DEBUG, "User %s mailbox set for update.\n",user);
9059 vms->updated = 1; /* set updated flag since mailbox changed */
9060 } else {
9061 if (option_debug > 2)
9062 ast_log (LOG_WARNING, "User %s mailbox not found for update.\n",user);
9066 static void init_vm_state(struct vm_state *vms)
9068 int x;
9069 vms->vmArrayIndex = 0;
9070 for (x = 0; x < 256; x++) {
9071 vms->msgArray[x] = 0;
9073 ast_mutex_init(&vms->lock);
9076 static void check_msgArray(struct vm_state *vms)
9078 int x;
9079 for (x = 0; x<256; x++) {
9080 if (vms->msgArray[x]!=0) {
9081 if (option_debug)
9082 ast_log (LOG_DEBUG, "Item %d set to %ld\n",x,vms->msgArray[x]);
9087 static void copy_msgArray(struct vm_state *dst, struct vm_state *src)
9089 int x;
9090 for (x = 0; x<256; x++) {
9091 dst->msgArray[x] = src->msgArray[x];
9095 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format)
9097 char *body_content;
9098 char *body_decoded;
9099 unsigned long len;
9100 unsigned long newlen;
9101 char filename[256];
9103 if (!body || body == NIL)
9104 return -1;
9105 body_content = mail_fetchbody (vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
9106 if (body_content != NIL) {
9107 snprintf(filename, sizeof(filename), "%s.%s", vms->fn, format);
9108 /* ast_log (LOG_DEBUG,body_content); */
9109 body_decoded = rfc822_base64 ((unsigned char *)body_content, len, &newlen);
9110 write_file (filename, (char *) body_decoded, newlen);
9112 return 0;
9115 /* get delimiter via mm_list callback */
9116 static void get_mailbox_delimiter(MAILSTREAM *stream) {
9117 char tmp[50];
9118 snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
9119 mail_list(stream, tmp, "*");
9122 /* Check Quota for user */
9123 static void check_quota(struct vm_state *vms, char *mailbox) {
9124 mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
9125 if (option_debug > 2)
9126 ast_log(LOG_DEBUG, "Mailbox name set to: %s, about to check quotas\n", mailbox);
9127 if (vms && vms->mailstream != NULL) {
9128 imap_getquotaroot(vms->mailstream, mailbox);
9129 } else {
9130 ast_log(LOG_WARNING,"Mailstream not available for mailbox: %s\n",mailbox);
9134 #endif /* IMAP_STORAGE */
9136 /* This is a workaround so that menuselect displays a proper description
9137 * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
9140 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
9141 .load = load_module,
9142 .unload = unload_module,
9143 .reload = reload,