update comment to match the state of the code
[asterisk-bristuff.git] / apps / app_voicemail.c
blob4a5f4ad064a7492268bd7a78bd3dfa8ad37f827e
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 #include "c-client.h"
74 #include "imap4r1.h"
75 #include "linkage.h"
76 #endif
77 #include "asterisk/lock.h"
78 #include "asterisk/file.h"
79 #include "asterisk/logger.h"
80 #include "asterisk/channel.h"
81 #include "asterisk/pbx.h"
82 #include "asterisk/options.h"
83 #include "asterisk/config.h"
84 #include "asterisk/say.h"
85 #include "asterisk/module.h"
86 #include "asterisk/adsi.h"
87 #include "asterisk/app.h"
88 #include "asterisk/manager.h"
89 #include "asterisk/dsp.h"
90 #include "asterisk/localtime.h"
91 #include "asterisk/cli.h"
92 #include "asterisk/utils.h"
93 #include "asterisk/stringfields.h"
94 #include "asterisk/smdi.h"
95 #ifdef ODBC_STORAGE
96 #include "asterisk/res_odbc.h"
97 #endif
99 #ifdef IMAP_STORAGE
100 AST_MUTEX_DEFINE_STATIC(imaptemp_lock);
101 static char imaptemp[1024];
103 static char imapserver[48];
104 static char imapport[8];
105 static char imapflags[128];
106 static char imapfolder[64];
107 static char authuser[32];
108 static char authpassword[42];
110 static int expungeonhangup = 1;
111 static char delimiter = '\0';
113 struct vm_state;
114 struct ast_vm_user;
116 static int init_mailstream (struct vm_state *vms, int box);
117 static void write_file (char *filename, char *buffer, unsigned long len);
118 /*static void status (MAILSTREAM *stream); */ /* No need for this. */
119 static char *get_header_by_tag(char *header, char *tag);
120 static void vm_imap_delete(int msgnum, struct vm_state *vms);
121 static char *get_user_by_mailbox(char *mailbox);
122 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
123 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
124 static void vmstate_insert(struct vm_state *vms);
125 static void vmstate_delete(struct vm_state *vms);
126 static void set_update(MAILSTREAM * stream);
127 static void init_vm_state(struct vm_state *vms);
128 static void check_msgArray(struct vm_state *vms);
129 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
130 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
131 static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num);
132 static void get_mailbox_delimiter(MAILSTREAM *stream);
133 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
134 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
135 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);
136 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box);
137 struct vmstate {
138 struct vm_state *vms;
139 struct vmstate *next;
141 AST_MUTEX_DEFINE_STATIC(vmstate_lock);
142 static struct vmstate *vmstates = NULL;
143 #endif
145 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
147 #define COMMAND_TIMEOUT 5000
148 /* Don't modify these here; set your umask at runtime instead */
149 #define VOICEMAIL_DIR_MODE 0777
150 #define VOICEMAIL_FILE_MODE 0666
151 #define CHUNKSIZE 65536
153 #define VOICEMAIL_CONFIG "voicemail.conf"
154 #define ASTERISK_USERNAME "asterisk"
156 /* Default mail command to mail voicemail. Change it with the
157 mailcmd= command in voicemail.conf */
158 #define SENDMAIL "/usr/sbin/sendmail -t"
160 #define INTRO "vm-intro"
162 #define MAXMSG 100
163 #define MAXMSGLIMIT 9999
165 #define BASEMAXINLINE 256
166 #define BASELINELEN 72
167 #define BASEMAXINLINE 256
168 #define eol "\r\n"
170 #define MAX_DATETIME_FORMAT 512
171 #define MAX_NUM_CID_CONTEXTS 10
173 #define VM_REVIEW (1 << 0)
174 #define VM_OPERATOR (1 << 1)
175 #define VM_SAYCID (1 << 2)
176 #define VM_SVMAIL (1 << 3)
177 #define VM_ENVELOPE (1 << 4)
178 #define VM_SAYDURATION (1 << 5)
179 #define VM_SKIPAFTERCMD (1 << 6)
180 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
181 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
182 #define VM_PBXSKIP (1 << 9)
183 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
184 #define VM_ATTACH (1 << 11)
185 #define VM_DELETE (1 << 12)
186 #define VM_ALLOCED (1 << 13)
187 #define VM_SEARCH (1 << 14)
188 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
189 #define ERROR_LOCK_PATH -100
192 enum {
193 OPT_SILENT = (1 << 0),
194 OPT_BUSY_GREETING = (1 << 1),
195 OPT_UNAVAIL_GREETING = (1 << 2),
196 OPT_RECORDGAIN = (1 << 3),
197 OPT_PREPEND_MAILBOX = (1 << 4),
198 OPT_PRIORITY_JUMP = (1 << 5),
199 OPT_AUTOPLAY = (1 << 6),
200 } vm_option_flags;
202 enum {
203 OPT_ARG_RECORDGAIN = 0,
204 OPT_ARG_PLAYFOLDER = 1,
205 /* This *must* be the last value in this enum! */
206 OPT_ARG_ARRAY_SIZE = 2,
207 } vm_option_args;
209 AST_APP_OPTIONS(vm_app_options, {
210 AST_APP_OPTION('s', OPT_SILENT),
211 AST_APP_OPTION('b', OPT_BUSY_GREETING),
212 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
213 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
214 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
215 AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
216 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
219 static int load_config(void);
221 /*! \page vmlang Voicemail Language Syntaxes Supported
223 \par Syntaxes supported, not really language codes.
224 \arg \b en - English
225 \arg \b de - German
226 \arg \b es - Spanish
227 \arg \b fr - French
228 \arg \b it = Italian
229 \arg \b nl - Dutch
230 \arg \b pt - Polish
231 \arg \b pt - Portuguese
232 \arg \b pt_BR - Portuguese (Brazil)
233 \arg \b gr - Greek
234 \arg \b no - Norwegian
235 \arg \b se - Swedish
236 \arg \b ua - Ukrainian
238 German requires the following additional soundfile:
239 \arg \b 1F einE (feminine)
241 Spanish requires the following additional soundfile:
242 \arg \b 1M un (masculine)
244 Dutch, Portuguese & Spanish require the following additional soundfiles:
245 \arg \b vm-INBOXs singular of 'new'
246 \arg \b vm-Olds singular of 'old/heard/read'
248 NB these are plural:
249 \arg \b vm-INBOX nieuwe (nl)
250 \arg \b vm-Old oude (nl)
252 Polish uses:
253 \arg \b vm-new-a 'new', feminine singular accusative
254 \arg \b vm-new-e 'new', feminine plural accusative
255 \arg \b vm-new-ych 'new', feminine plural genitive
256 \arg \b vm-old-a 'old', feminine singular accusative
257 \arg \b vm-old-e 'old', feminine plural accusative
258 \arg \b vm-old-ych 'old', feminine plural genitive
259 \arg \b digits/1-a 'one', not always same as 'digits/1'
260 \arg \b digits/2-ie 'two', not always same as 'digits/2'
262 Swedish uses:
263 \arg \b vm-nytt singular of 'new'
264 \arg \b vm-nya plural of 'new'
265 \arg \b vm-gammalt singular of 'old'
266 \arg \b vm-gamla plural of 'old'
267 \arg \b digits/ett 'one', not always same as 'digits/1'
269 Norwegian uses:
270 \arg \b vm-ny singular of 'new'
271 \arg \b vm-nye plural of 'new'
272 \arg \b vm-gammel singular of 'old'
273 \arg \b vm-gamle plural of 'old'
275 Dutch also uses:
276 \arg \b nl-om 'at'?
278 Spanish also uses:
279 \arg \b vm-youhaveno
281 Ukrainian requires the following additional soundfile:
282 \arg \b vm-nove 'nove'
283 \arg \b vm-stare 'stare'
284 \arg \b digits/ua/1e 'odne'
286 Italian requires the following additional soundfile:
288 For vm_intro_it:
289 \arg \b vm-nuovo new
290 \arg \b vm-nuovi new plural
291 \arg \b vm-vecchio old
292 \arg \b vm-vecchi old plural
294 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
295 spelled among others when you have to change folder. For the above reasons, vm-INBOX
296 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
300 struct baseio {
301 int iocp;
302 int iolen;
303 int linelength;
304 int ateof;
305 unsigned char iobuf[BASEMAXINLINE];
308 /*! Structure for linked list of users */
309 struct ast_vm_user {
310 char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
311 char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
312 char password[80]; /*!< Secret pin code, numbers only */
313 char fullname[80]; /*!< Full name, for directory app */
314 char email[80]; /*!< E-mail address */
315 char pager[80]; /*!< E-mail address to pager (no attachment) */
316 char serveremail[80]; /*!< From: Mail address */
317 char mailcmd[160]; /*!< Configurable mail command */
318 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
319 char zonetag[80]; /*!< Time zone */
320 char callback[80];
321 char dialout[80];
322 char uniqueid[20]; /*!< Unique integer identifier */
323 char exit[80];
324 char attachfmt[20]; /*!< Attachment format */
325 unsigned int flags; /*!< VM_ flags */
326 int saydurationm;
327 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
328 #ifdef IMAP_STORAGE
329 char imapuser[80]; /* IMAP server login */
330 char imappassword[80]; /* IMAP server password if authpassword not defined */
331 #endif
332 double volgain; /*!< Volume gain for voicemails sent via email */
333 AST_LIST_ENTRY(ast_vm_user) list;
336 struct vm_zone {
337 AST_LIST_ENTRY(vm_zone) list;
338 char name[80];
339 char timezone[80];
340 char msg_format[512];
343 struct vm_state {
344 char curbox[80];
345 char username[80];
346 char curdir[PATH_MAX];
347 char vmbox[PATH_MAX];
348 char fn[PATH_MAX];
349 char fn2[PATH_MAX];
350 int *deleted;
351 int *heard;
352 int curmsg;
353 int lastmsg;
354 int newmessages;
355 int oldmessages;
356 int starting;
357 int repeats;
358 #ifdef IMAP_STORAGE
359 int updated; /* decremented on each mail check until 1 -allows delay */
360 long msgArray[256];
361 MAILSTREAM *mailstream;
362 int vmArrayIndex;
363 char imapuser[80]; /* IMAP server login */
364 int interactive;
365 unsigned int quota_limit;
366 unsigned int quota_usage;
367 struct vm_state *persist_vms;
368 #endif
370 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);
371 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
372 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
373 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
374 signed char record_gain, struct vm_state *vms);
375 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
376 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
377 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
378 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);
379 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
380 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
381 #endif
382 static void apply_options(struct ast_vm_user *vmu, const char *options);
384 #ifdef ODBC_STORAGE
385 static char odbc_database[80];
386 static char odbc_table[80];
387 #define RETRIEVE(a,b) retrieve_file(a,b)
388 #define DISPOSE(a,b) remove_file(a,b)
389 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
390 #define EXISTS(a,b,c,d) (message_exists(a,b))
391 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
392 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
393 #define DELETE(a,b,c) (delete_file(a,b))
394 #else
395 #ifdef IMAP_STORAGE
396 #define RETRIEVE(a,b)
397 #define DISPOSE(a,b)
398 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
399 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
400 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
401 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
402 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
403 #define DELETE(a,b,c) (vm_delete(c))
404 #else
405 #define RETRIEVE(a,b)
406 #define DISPOSE(a,b)
407 #define STORE(a,b,c,d,e,f,g,h,i)
408 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
409 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
410 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
411 #define DELETE(a,b,c) (vm_delete(c))
412 #endif
413 #endif
415 static char VM_SPOOL_DIR[PATH_MAX];
417 static char ext_pass_cmd[128];
419 int my_umask;
421 #if ODBC_STORAGE
422 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
423 #elif IMAP_STORAGE
424 #define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
425 #else
426 #define tdesc "Comedian Mail (Voicemail System)"
427 #endif
429 static char userscontext[AST_MAX_EXTENSION] = "default";
431 static char *addesc = "Comedian Mail";
433 static char *synopsis_vm =
434 "Leave a Voicemail message";
436 static char *descrip_vm =
437 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
438 "application allows the calling party to leave a message for the specified\n"
439 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
440 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
441 "specified mailbox does not exist.\n"
442 " The Voicemail application will exit if any of the following DTMF digits are\n"
443 "received:\n"
444 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
445 " * - Jump to the 'a' extension in the current dialplan context.\n"
446 " This application will set the following channel variable upon completion:\n"
447 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
448 " application. The possible values are:\n"
449 " SUCCESS | USEREXIT | FAILED\n\n"
450 " Options:\n"
451 " b - Play the 'busy' greeting to the calling party.\n"
452 " g(#) - Use the specified amount of gain when recording the voicemail\n"
453 " message. The units are whole-number decibels (dB).\n"
454 " s - Skip the playback of instructions for leaving a message to the\n"
455 " calling party.\n"
456 " u - Play the 'unavailable' greeting.\n"
457 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
458 " error occurs.\n";
460 static char *synopsis_vmain =
461 "Check Voicemail messages";
463 static char *descrip_vmain =
464 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
465 "calling party to check voicemail messages. A specific mailbox, and optional\n"
466 "corresponding context, may be specified. If a mailbox is not provided, the\n"
467 "calling party will be prompted to enter one. If a context is not specified,\n"
468 "the 'default' context will be used.\n\n"
469 " Options:\n"
470 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
471 " is entered by the caller.\n"
472 " g(#) - Use the specified amount of gain when recording a voicemail\n"
473 " message. The units are whole-number decibels (dB).\n"
474 " s - Skip checking the passcode for the mailbox.\n"
475 " a(#) - Skip folder prompt and go directly to folder specified.\n"
476 " Defaults to INBOX\n";
478 static char *synopsis_vm_box_exists =
479 "Check to see if Voicemail mailbox exists";
481 static char *descrip_vm_box_exists =
482 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
483 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
484 "will be used.\n"
485 " This application will set the following channel variable upon completion:\n"
486 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
487 " MailboxExists application. Possible values include:\n"
488 " SUCCESS | FAILED\n\n"
489 " Options:\n"
490 " j - Jump to priority n+101 if the mailbox is found.\n";
492 static char *synopsis_vmauthenticate =
493 "Authenticate with Voicemail passwords";
495 static char *descrip_vmauthenticate =
496 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
497 "same way as the Authenticate application, but the passwords are taken from\n"
498 "voicemail.conf.\n"
499 " If the mailbox is specified, only that mailbox's password will be considered\n"
500 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
501 "be set with the authenticated mailbox.\n\n"
502 " Options:\n"
503 " s - Skip playing the initial prompts.\n";
505 /* Leave a message */
506 static char *app = "VoiceMail";
508 /* Check mail, control, etc */
509 static char *app2 = "VoiceMailMain";
511 static char *app3 = "MailboxExists";
512 static char *app4 = "VMAuthenticate";
514 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
515 static AST_LIST_HEAD_STATIC(zones, vm_zone);
516 static int maxsilence;
517 static int maxmsg;
518 static int silencethreshold = 128;
519 static char serveremail[80];
520 static char mailcmd[160]; /* Configurable mail cmd */
521 static char externnotify[160];
522 static struct ast_smdi_interface *smdi_iface = NULL;
523 static char vmfmts[80];
524 static double volgain;
525 static int vmminmessage;
526 static int vmmaxmessage;
527 static int maxgreet;
528 static int skipms;
529 static int maxlogins;
531 static struct ast_flags globalflags = {0};
533 static int saydurationminfo;
535 static char dialcontext[AST_MAX_CONTEXT];
536 static char callcontext[AST_MAX_CONTEXT];
537 static char exitcontext[AST_MAX_CONTEXT];
539 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
542 static char *emailbody = NULL;
543 static char *emailsubject = NULL;
544 static char *pagerbody = NULL;
545 static char *pagersubject = NULL;
546 static char fromstring[100];
547 static char pagerfromstring[100];
548 static char emailtitle[100];
549 static char charset[32] = "ISO-8859-1";
551 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
552 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
553 static int adsiver = 1;
554 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
557 static void populate_defaults(struct ast_vm_user *vmu)
559 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
560 if (saydurationminfo)
561 vmu->saydurationm = saydurationminfo;
562 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
563 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
564 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
565 if (maxmsg)
566 vmu->maxmsg = maxmsg;
567 vmu->volgain = volgain;
570 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
572 int x;
573 if (!strcasecmp(var, "attach")) {
574 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
575 } else if (!strcasecmp(var, "attachfmt")) {
576 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
577 } else if (!strcasecmp(var, "serveremail")) {
578 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
579 } else if (!strcasecmp(var, "language")) {
580 ast_copy_string(vmu->language, value, sizeof(vmu->language));
581 } else if (!strcasecmp(var, "tz")) {
582 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
583 #ifdef IMAP_STORAGE
584 } else if (!strcasecmp(var, "imapuser")) {
585 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
586 } else if (!strcasecmp(var, "imappassword")) {
587 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
588 #endif
589 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
590 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
591 } else if (!strcasecmp(var, "saycid")){
592 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
593 } else if (!strcasecmp(var,"sendvoicemail")){
594 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
595 } else if (!strcasecmp(var, "review")){
596 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
597 } else if (!strcasecmp(var, "tempgreetwarn")){
598 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
599 } else if (!strcasecmp(var, "operator")){
600 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
601 } else if (!strcasecmp(var, "envelope")){
602 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
603 } else if (!strcasecmp(var, "sayduration")){
604 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
605 } else if (!strcasecmp(var, "saydurationm")){
606 if (sscanf(value, "%d", &x) == 1) {
607 vmu->saydurationm = x;
608 } else {
609 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
611 } else if (!strcasecmp(var, "forcename")){
612 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
613 } else if (!strcasecmp(var, "forcegreetings")){
614 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
615 } else if (!strcasecmp(var, "callback")) {
616 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
617 } else if (!strcasecmp(var, "dialout")) {
618 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
619 } else if (!strcasecmp(var, "exitcontext")) {
620 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
621 } else if (!strcasecmp(var, "maxmsg")) {
622 vmu->maxmsg = atoi(value);
623 if (vmu->maxmsg <= 0) {
624 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
625 vmu->maxmsg = MAXMSG;
626 } else if (vmu->maxmsg > MAXMSGLIMIT) {
627 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
628 vmu->maxmsg = MAXMSGLIMIT;
630 } else if (!strcasecmp(var, "volgain")) {
631 sscanf(value, "%lf", &vmu->volgain);
632 } else if (!strcasecmp(var, "options")) {
633 apply_options(vmu, value);
637 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
639 int res;
640 if (!ast_strlen_zero(vmu->uniqueid)) {
641 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
642 if (res > 0) {
643 ast_copy_string(vmu->password, password, sizeof(vmu->password));
644 res = 0;
645 } else if (!res) {
646 res = -1;
648 return res;
650 return -1;
653 static void apply_options(struct ast_vm_user *vmu, const char *options)
654 { /* Destructively Parse options and apply */
655 char *stringp;
656 char *s;
657 char *var, *value;
658 stringp = ast_strdupa(options);
659 while ((s = strsep(&stringp, "|"))) {
660 value = s;
661 if ((var = strsep(&value, "=")) && value) {
662 apply_option(vmu, var, value);
667 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
669 struct ast_variable *tmp;
670 tmp = var;
671 while (tmp) {
672 if (!strcasecmp(tmp->name, "vmsecret")) {
673 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
674 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
675 if (ast_strlen_zero(retval->password))
676 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
677 } else if (!strcasecmp(tmp->name, "uniqueid")) {
678 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
679 } else if (!strcasecmp(tmp->name, "pager")) {
680 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
681 } else if (!strcasecmp(tmp->name, "email")) {
682 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
683 } else if (!strcasecmp(tmp->name, "fullname")) {
684 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
685 } else if (!strcasecmp(tmp->name, "context")) {
686 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
687 #ifdef IMAP_STORAGE
688 } else if (!strcasecmp(tmp->name, "imapuser")) {
689 ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
690 } else if (!strcasecmp(tmp->name, "imappassword")) {
691 ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
692 #endif
693 } else
694 apply_option(retval, tmp->name, tmp->value);
695 tmp = tmp->next;
699 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
701 struct ast_variable *var;
702 struct ast_vm_user *retval;
704 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
705 if (!ivm)
706 ast_set_flag(retval, VM_ALLOCED);
707 else
708 memset(retval, 0, sizeof(*retval));
709 if (mailbox)
710 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
711 populate_defaults(retval);
712 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
713 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
714 else
715 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
716 if (var) {
717 apply_options_full(retval, var);
718 ast_variables_destroy(var);
719 } else {
720 if (!ivm)
721 free(retval);
722 retval = NULL;
725 return retval;
728 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
730 /* This function could be made to generate one from a database, too */
731 struct ast_vm_user *vmu=NULL, *cur;
732 AST_LIST_LOCK(&users);
734 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
735 context = "default";
737 AST_LIST_TRAVERSE(&users, cur, list) {
738 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
739 break;
740 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
741 break;
743 if (cur) {
744 /* Make a copy, so that on a reload, we have no race */
745 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
746 memcpy(vmu, cur, sizeof(*vmu));
747 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
748 AST_LIST_NEXT(vmu, list) = NULL;
750 } else
751 vmu = find_user_realtime(ivm, context, mailbox);
752 AST_LIST_UNLOCK(&users);
753 return vmu;
756 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
758 /* This function could be made to generate one from a database, too */
759 struct ast_vm_user *cur;
760 int res = -1;
761 AST_LIST_LOCK(&users);
762 AST_LIST_TRAVERSE(&users, cur, list) {
763 if ((!context || !strcasecmp(context, cur->context)) &&
764 (!strcasecmp(mailbox, cur->mailbox)))
765 break;
767 if (cur) {
768 ast_copy_string(cur->password, newpass, sizeof(cur->password));
769 res = 0;
771 AST_LIST_UNLOCK(&users);
772 return res;
775 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
777 struct ast_config *cfg=NULL;
778 struct ast_variable *var=NULL;
779 struct ast_category *cat=NULL;
780 char *category=NULL, *value=NULL, *new=NULL;
781 const char *tmp=NULL;
783 if (!change_password_realtime(vmu, newpassword))
784 return;
786 /* check voicemail.conf */
787 if ((cfg = ast_config_load_with_comments(VOICEMAIL_CONFIG))) {
788 while ((category = ast_category_browse(cfg, category))) {
789 if (!strcasecmp(category, vmu->context)) {
790 tmp = ast_variable_retrieve(cfg, category, vmu->mailbox);
791 if (!tmp) {
792 ast_log(LOG_WARNING, "We could not find the mailbox.\n");
793 break;
795 value = strstr(tmp,",");
796 if (!value) {
797 ast_log(LOG_WARNING, "variable has bad format.\n");
798 break;
800 new = alloca((strlen(value)+strlen(newpassword)+1));
801 sprintf(new,"%s%s", newpassword, value);
802 if (!(cat = ast_category_get(cfg, category))) {
803 ast_log(LOG_WARNING, "Failed to get category structure.\n");
804 break;
806 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
809 /* save the results */
810 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
811 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
812 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
814 category = NULL;
815 var = NULL;
816 /* check users.conf and update the password stored for the mailbox*/
817 /* if no vmsecret entry exists create one. */
818 if ((cfg = ast_config_load_with_comments("users.conf"))) {
819 if (option_debug > 3)
820 ast_log(LOG_DEBUG, "we are looking for %s\n", vmu->mailbox);
821 while ((category = ast_category_browse(cfg, category))) {
822 if (option_debug > 3)
823 ast_log(LOG_DEBUG, "users.conf: %s\n", category);
824 if (!strcasecmp(category, vmu->mailbox)) {
825 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
826 if (option_debug > 3)
827 ast_log(LOG_DEBUG, "looks like we need to make vmsecret!\n");
828 var = ast_variable_new("vmsecret", newpassword);
830 new = alloca(strlen(newpassword)+1);
831 sprintf(new, "%s", newpassword);
832 if (!(cat = ast_category_get(cfg, category))) {
833 if (option_debug > 3)
834 ast_log(LOG_DEBUG, "failed to get category!\n");
835 break;
837 if (!var)
838 ast_variable_update(cat, "vmsecret", new, NULL, 0);
839 else
840 ast_variable_append(cat, var);
843 /* save the results and clean things up */
844 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
845 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
846 config_text_file_save("users.conf", cfg, "AppVoicemail");
850 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
852 char buf[255];
853 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
854 if (!ast_safe_system(buf))
855 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
858 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
860 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
863 #ifdef IMAP_STORAGE
864 static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num)
866 if (mkdir(dir, 01777) && (errno != EEXIST)) {
867 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
868 return snprintf(dest, len, "%s/msg%04d", dir, num);
870 return snprintf(dest, len, "%s/msg%04d", dir, num);
873 static void vm_imap_delete(int msgnum, struct vm_state *vms)
875 unsigned long messageNum = 0;
876 char arg[10];
878 /* find real message number based on msgnum */
879 /* this may be an index into vms->msgArray based on the msgnum. */
881 messageNum = vms->msgArray[msgnum];
882 if (messageNum == 0) {
883 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
884 return;
886 if(option_debug > 2)
887 ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
888 /* delete message */
889 snprintf (arg, sizeof(arg), "%lu",messageNum);
890 mail_setflag (vms->mailstream,arg,"\\DELETED");
893 #endif
894 static int make_file(char *dest, int len, char *dir, int num)
896 return snprintf(dest, len, "%s/msg%04d", dir, num);
899 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
900 * \param dest String. base directory.
901 * \param len Length of dest.
902 * \param context String. Ignored if is null or empty string.
903 * \param ext String. Ignored if is null or empty string.
904 * \param folder String. Ignored if is null or empty string.
905 * \return -1 on failure, 0 on success.
907 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
909 mode_t mode = VOICEMAIL_DIR_MODE;
911 if (!ast_strlen_zero(context)) {
912 make_dir(dest, len, context, "", "");
913 if (mkdir(dest, mode) && errno != EEXIST) {
914 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
915 return -1;
918 if (!ast_strlen_zero(ext)) {
919 make_dir(dest, len, context, ext, "");
920 if (mkdir(dest, mode) && errno != EEXIST) {
921 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
922 return -1;
925 if (!ast_strlen_zero(folder)) {
926 make_dir(dest, len, context, ext, folder);
927 if (mkdir(dest, mode) && errno != EEXIST) {
928 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
929 return -1;
932 return 0;
935 /* only return failure if ast_lock_path returns 'timeout',
936 not if the path does not exist or any other reason
938 static int vm_lock_path(const char *path)
940 switch (ast_lock_path(path)) {
941 case AST_LOCK_TIMEOUT:
942 return -1;
943 default:
944 return 0;
949 #ifdef ODBC_STORAGE
950 struct generic_prepare_struct {
951 char *sql;
952 int argc;
953 char **argv;
956 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
958 struct generic_prepare_struct *gps = data;
959 int res, i;
960 SQLHSTMT stmt;
962 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
963 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
964 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
965 return NULL;
967 res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
968 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
969 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
970 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
971 return NULL;
973 for (i = 0; i < gps->argc; i++)
974 SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
976 return stmt;
979 static int retrieve_file(char *dir, int msgnum)
981 int x = 0;
982 int res;
983 int fd=-1;
984 size_t fdlen = 0;
985 void *fdm = MAP_FAILED;
986 SQLSMALLINT colcount=0;
987 SQLHSTMT stmt;
988 char sql[PATH_MAX];
989 char fmt[80]="";
990 char *c;
991 char coltitle[256];
992 SQLSMALLINT collen;
993 SQLSMALLINT datatype;
994 SQLSMALLINT decimaldigits;
995 SQLSMALLINT nullable;
996 SQLULEN colsize;
997 SQLLEN colsize2;
998 FILE *f=NULL;
999 char rowdata[80];
1000 char fn[PATH_MAX];
1001 char full_fn[PATH_MAX];
1002 char msgnums[80];
1003 char *argv[] = { dir, msgnums };
1004 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1006 struct odbc_obj *obj;
1007 obj = ast_odbc_request_obj(odbc_database, 0);
1008 if (obj) {
1009 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1010 c = strchr(fmt, '|');
1011 if (c)
1012 *c = '\0';
1013 if (!strcasecmp(fmt, "wav49"))
1014 strcpy(fmt, "WAV");
1015 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1016 if (msgnum > -1)
1017 make_file(fn, sizeof(fn), dir, msgnum);
1018 else
1019 ast_copy_string(fn, dir, sizeof(fn));
1020 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1022 if (!(f = fopen(full_fn, "w+"))) {
1023 ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
1024 goto yuck;
1027 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1028 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1029 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1030 if (!stmt) {
1031 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1032 ast_odbc_release_obj(obj);
1033 goto yuck;
1035 res = SQLFetch(stmt);
1036 if (res == SQL_NO_DATA) {
1037 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1038 ast_odbc_release_obj(obj);
1039 goto yuck;
1041 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1042 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1043 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1044 ast_odbc_release_obj(obj);
1045 goto yuck;
1047 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, 0770);
1048 if (fd < 0) {
1049 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1050 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1051 ast_odbc_release_obj(obj);
1052 goto yuck;
1054 res = SQLNumResultCols(stmt, &colcount);
1055 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1056 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1057 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1058 ast_odbc_release_obj(obj);
1059 goto yuck;
1061 if (f)
1062 fprintf(f, "[message]\n");
1063 for (x=0;x<colcount;x++) {
1064 rowdata[0] = '\0';
1065 collen = sizeof(coltitle);
1066 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
1067 &datatype, &colsize, &decimaldigits, &nullable);
1068 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1069 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1070 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1071 ast_odbc_release_obj(obj);
1072 goto yuck;
1074 if (!strcasecmp(coltitle, "recording")) {
1075 off_t offset;
1076 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
1077 fdlen = colsize2;
1078 if (fd > -1) {
1079 char tmp[1]="";
1080 lseek(fd, fdlen - 1, SEEK_SET);
1081 if (write(fd, tmp, 1) != 1) {
1082 close(fd);
1083 fd = -1;
1084 continue;
1086 /* Read out in small chunks */
1087 for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1088 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
1089 ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1090 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1091 ast_odbc_release_obj(obj);
1092 goto yuck;
1093 } else {
1094 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
1095 munmap(fdm, CHUNKSIZE);
1096 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1097 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1098 unlink(full_fn);
1099 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1100 ast_odbc_release_obj(obj);
1101 goto yuck;
1105 truncate(full_fn, fdlen);
1107 } else {
1108 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1109 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1110 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1111 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1112 ast_odbc_release_obj(obj);
1113 goto yuck;
1115 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1116 fprintf(f, "%s=%s\n", coltitle, rowdata);
1119 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1120 ast_odbc_release_obj(obj);
1121 } else
1122 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1123 yuck:
1124 if (f)
1125 fclose(f);
1126 if (fd > -1)
1127 close(fd);
1128 return x - 1;
1131 static int remove_file(char *dir, int msgnum)
1133 char fn[PATH_MAX];
1134 char full_fn[PATH_MAX];
1135 char msgnums[80];
1137 if (msgnum > -1) {
1138 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1139 make_file(fn, sizeof(fn), dir, msgnum);
1140 } else
1141 ast_copy_string(fn, dir, sizeof(fn));
1142 ast_filedelete(fn, NULL);
1143 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1144 unlink(full_fn);
1145 return 0;
1148 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1150 int x = 0;
1151 int res;
1152 SQLHSTMT stmt;
1153 char sql[PATH_MAX];
1154 char rowdata[20];
1155 char *argv[] = { dir };
1156 struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
1158 struct odbc_obj *obj;
1159 obj = ast_odbc_request_obj(odbc_database, 0);
1160 if (obj) {
1161 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1162 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1163 if (!stmt) {
1164 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1165 ast_odbc_release_obj(obj);
1166 goto yuck;
1168 res = SQLFetch(stmt);
1169 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1170 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1171 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1172 ast_odbc_release_obj(obj);
1173 goto yuck;
1175 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1176 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1177 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1178 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1179 ast_odbc_release_obj(obj);
1180 goto yuck;
1182 if (sscanf(rowdata, "%d", &x) != 1)
1183 ast_log(LOG_WARNING, "Failed to read message count!\n");
1184 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1185 ast_odbc_release_obj(obj);
1186 } else
1187 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1188 yuck:
1189 return x - 1;
1192 static int message_exists(char *dir, int msgnum)
1194 int x = 0;
1195 int res;
1196 SQLHSTMT stmt;
1197 char sql[PATH_MAX];
1198 char rowdata[20];
1199 char msgnums[20];
1200 char *argv[] = { dir, msgnums };
1201 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1203 struct odbc_obj *obj;
1204 obj = ast_odbc_request_obj(odbc_database, 0);
1205 if (obj) {
1206 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1207 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1208 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1209 if (!stmt) {
1210 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1211 ast_odbc_release_obj(obj);
1212 goto yuck;
1214 res = SQLFetch(stmt);
1215 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1216 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1217 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1218 ast_odbc_release_obj(obj);
1219 goto yuck;
1221 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1222 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1223 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1224 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1225 ast_odbc_release_obj(obj);
1226 goto yuck;
1228 if (sscanf(rowdata, "%d", &x) != 1)
1229 ast_log(LOG_WARNING, "Failed to read message count!\n");
1230 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1231 ast_odbc_release_obj(obj);
1232 } else
1233 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1234 yuck:
1235 return x;
1238 static int count_messages(struct ast_vm_user *vmu, char *dir)
1240 return last_message_index(vmu, dir) + 1;
1243 static void delete_file(char *sdir, int smsg)
1245 SQLHSTMT stmt;
1246 char sql[PATH_MAX];
1247 char msgnums[20];
1248 char *argv[] = { sdir, msgnums };
1249 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1251 struct odbc_obj *obj;
1252 obj = ast_odbc_request_obj(odbc_database, 0);
1253 if (obj) {
1254 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1255 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1256 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1257 if (!stmt)
1258 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1259 else
1260 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1261 ast_odbc_release_obj(obj);
1262 } else
1263 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1264 return;
1267 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1269 SQLHSTMT stmt;
1270 char sql[512];
1271 char msgnums[20];
1272 char msgnumd[20];
1273 struct odbc_obj *obj;
1274 char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
1275 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1277 delete_file(ddir, dmsg);
1278 obj = ast_odbc_request_obj(odbc_database, 0);
1279 if (obj) {
1280 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1281 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1282 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);
1283 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1284 if (!stmt)
1285 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1286 else
1287 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1288 ast_odbc_release_obj(obj);
1289 } else
1290 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1291 return;
1294 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1296 int x = 0;
1297 int res;
1298 int fd = -1;
1299 void *fdm = MAP_FAILED;
1300 size_t fdlen = -1;
1301 SQLHSTMT stmt;
1302 SQLLEN len;
1303 char sql[PATH_MAX];
1304 char msgnums[20];
1305 char fn[PATH_MAX];
1306 char full_fn[PATH_MAX];
1307 char fmt[80]="";
1308 char *c;
1309 const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1310 const char *category = "";
1311 struct ast_config *cfg=NULL;
1312 struct odbc_obj *obj;
1314 delete_file(dir, msgnum);
1315 obj = ast_odbc_request_obj(odbc_database, 0);
1316 if (obj) {
1317 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1318 c = strchr(fmt, '|');
1319 if (c)
1320 *c = '\0';
1321 if (!strcasecmp(fmt, "wav49"))
1322 strcpy(fmt, "WAV");
1323 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1324 if (msgnum > -1)
1325 make_file(fn, sizeof(fn), dir, msgnum);
1326 else
1327 ast_copy_string(fn, dir, sizeof(fn));
1328 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1329 cfg = ast_config_load(full_fn);
1330 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1331 fd = open(full_fn, O_RDWR);
1332 if (fd < 0) {
1333 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1334 ast_odbc_release_obj(obj);
1335 goto yuck;
1337 if (cfg) {
1338 context = ast_variable_retrieve(cfg, "message", "context");
1339 if (!context) context = "";
1340 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1341 if (!macrocontext) macrocontext = "";
1342 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1343 if (!callerid) callerid = "";
1344 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1345 if (!origtime) origtime = "";
1346 duration = ast_variable_retrieve(cfg, "message", "duration");
1347 if (!duration) duration = "";
1348 category = ast_variable_retrieve(cfg, "message", "category");
1349 if (!category) category = "";
1351 fdlen = lseek(fd, 0, SEEK_END);
1352 lseek(fd, 0, SEEK_SET);
1353 printf("Length is %zd\n", fdlen);
1354 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1355 if (fdm == MAP_FAILED) {
1356 ast_log(LOG_WARNING, "Memory map failed!\n");
1357 ast_odbc_release_obj(obj);
1358 goto yuck;
1360 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1361 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1362 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1363 ast_odbc_release_obj(obj);
1364 goto yuck;
1366 if (!ast_strlen_zero(category))
1367 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1368 else
1369 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1370 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1371 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1372 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1373 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1374 ast_odbc_release_obj(obj);
1375 goto yuck;
1377 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1378 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1379 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1380 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1381 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1382 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1383 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1384 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1385 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1386 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1387 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1388 if (!ast_strlen_zero(category))
1389 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1390 res = ast_odbc_smart_execute(obj, stmt);
1391 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1392 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1393 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1394 ast_odbc_release_obj(obj);
1395 goto yuck;
1397 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1398 ast_odbc_release_obj(obj);
1399 } else
1400 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1401 yuck:
1402 if (cfg)
1403 ast_config_destroy(cfg);
1404 if (fdm != MAP_FAILED)
1405 munmap(fdm, fdlen);
1406 if (fd > -1)
1407 close(fd);
1408 return x;
1411 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1413 SQLHSTMT stmt;
1414 char sql[PATH_MAX];
1415 char msgnums[20];
1416 char msgnumd[20];
1417 struct odbc_obj *obj;
1418 char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
1419 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1421 delete_file(ddir, dmsg);
1422 obj = ast_odbc_request_obj(odbc_database, 0);
1423 if (obj) {
1424 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1425 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1426 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1427 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1428 if (!stmt)
1429 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1430 else
1431 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1432 ast_odbc_release_obj(obj);
1433 } else
1434 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1435 return;
1438 #else
1439 #ifndef IMAP_STORAGE
1440 static int count_messages(struct ast_vm_user *vmu, char *dir)
1442 /* Find all .txt files - even if they are not in sequence from 0000 */
1444 int vmcount = 0;
1445 DIR *vmdir = NULL;
1446 struct dirent *vment = NULL;
1448 if (vm_lock_path(dir))
1449 return ERROR_LOCK_PATH;
1451 if ((vmdir = opendir(dir))) {
1452 while ((vment = readdir(vmdir))) {
1453 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1454 vmcount++;
1456 closedir(vmdir);
1458 ast_unlock_path(dir);
1460 return vmcount;
1463 static void rename_file(char *sfn, char *dfn)
1465 char stxt[PATH_MAX];
1466 char dtxt[PATH_MAX];
1467 ast_filerename(sfn,dfn,NULL);
1468 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1469 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1470 rename(stxt, dtxt);
1473 static int copy(char *infile, char *outfile)
1475 int ifd;
1476 int ofd;
1477 int res;
1478 int len;
1479 char buf[4096];
1481 #ifdef HARDLINK_WHEN_POSSIBLE
1482 /* Hard link if possible; saves disk space & is faster */
1483 if (link(infile, outfile)) {
1484 #endif
1485 if ((ifd = open(infile, O_RDONLY)) < 0) {
1486 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1487 return -1;
1489 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1490 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1491 close(ifd);
1492 return -1;
1494 do {
1495 len = read(ifd, buf, sizeof(buf));
1496 if (len < 0) {
1497 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1498 close(ifd);
1499 close(ofd);
1500 unlink(outfile);
1502 if (len) {
1503 res = write(ofd, buf, len);
1504 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1505 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1506 close(ifd);
1507 close(ofd);
1508 unlink(outfile);
1511 } while (len);
1512 close(ifd);
1513 close(ofd);
1514 return 0;
1515 #ifdef HARDLINK_WHEN_POSSIBLE
1516 } else {
1517 /* Hard link succeeded */
1518 return 0;
1520 #endif
1523 static void copy_file(char *frompath, char *topath)
1525 char frompath2[PATH_MAX], topath2[PATH_MAX];
1526 ast_filecopy(frompath, topath, NULL);
1527 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1528 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1529 copy(frompath2, topath2);
1531 #endif
1533 * A negative return value indicates an error.
1535 #if (!defined(IMAP_STORAGE) && !defined(ODBC_STORAGE))
1536 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1538 int x;
1539 char fn[PATH_MAX];
1541 if (vm_lock_path(dir))
1542 return ERROR_LOCK_PATH;
1544 for (x = 0; x < vmu->maxmsg; x++) {
1545 make_file(fn, sizeof(fn), dir, x);
1546 if (ast_fileexists(fn, NULL, NULL) < 1)
1547 break;
1549 ast_unlock_path(dir);
1551 return x - 1;
1553 #endif
1554 #endif
1556 #ifndef ODBC_STORAGE
1557 static int vm_delete(char *file)
1559 char *txt;
1560 int txtsize = 0;
1562 txtsize = (strlen(file) + 5)*sizeof(char);
1563 txt = alloca(txtsize);
1564 /* Sprintf here would safe because we alloca'd exactly the right length,
1565 * but trying to eliminate all sprintf's anyhow
1567 snprintf(txt, txtsize, "%s.txt", file);
1568 unlink(txt);
1569 return ast_filedelete(file, NULL);
1571 #endif
1573 static int inbuf(struct baseio *bio, FILE *fi)
1575 int l;
1577 if (bio->ateof)
1578 return 0;
1580 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1581 if (ferror(fi))
1582 return -1;
1584 bio->ateof = 1;
1585 return 0;
1588 bio->iolen= l;
1589 bio->iocp= 0;
1591 return 1;
1594 static int inchar(struct baseio *bio, FILE *fi)
1596 if (bio->iocp>=bio->iolen) {
1597 if (!inbuf(bio, fi))
1598 return EOF;
1601 return bio->iobuf[bio->iocp++];
1604 static int ochar(struct baseio *bio, int c, FILE *so)
1606 if (bio->linelength>=BASELINELEN) {
1607 if (fputs(eol,so)==EOF)
1608 return -1;
1610 bio->linelength= 0;
1613 if (putc(((unsigned char)c),so)==EOF)
1614 return -1;
1616 bio->linelength++;
1618 return 1;
1621 static int base_encode(char *filename, FILE *so)
1623 unsigned char dtable[BASEMAXINLINE];
1624 int i,hiteof= 0;
1625 FILE *fi;
1626 struct baseio bio;
1628 memset(&bio, 0, sizeof(bio));
1629 bio.iocp = BASEMAXINLINE;
1631 if (!(fi = fopen(filename, "rb"))) {
1632 ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
1633 return -1;
1636 for (i= 0;i<9;i++) {
1637 dtable[i]= 'A'+i;
1638 dtable[i+9]= 'J'+i;
1639 dtable[26+i]= 'a'+i;
1640 dtable[26+i+9]= 'j'+i;
1642 for (i= 0;i<8;i++) {
1643 dtable[i+18]= 'S'+i;
1644 dtable[26+i+18]= 's'+i;
1646 for (i= 0;i<10;i++) {
1647 dtable[52+i]= '0'+i;
1649 dtable[62]= '+';
1650 dtable[63]= '/';
1652 while (!hiteof){
1653 unsigned char igroup[3],ogroup[4];
1654 int c,n;
1656 igroup[0]= igroup[1]= igroup[2]= 0;
1658 for (n= 0;n<3;n++) {
1659 if ((c = inchar(&bio, fi)) == EOF) {
1660 hiteof= 1;
1661 break;
1664 igroup[n]= (unsigned char)c;
1667 if (n> 0) {
1668 ogroup[0]= dtable[igroup[0]>>2];
1669 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1670 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1671 ogroup[3]= dtable[igroup[2]&0x3F];
1673 if (n<3) {
1674 ogroup[3]= '=';
1676 if (n<2)
1677 ogroup[2]= '=';
1680 for (i= 0;i<4;i++)
1681 ochar(&bio, ogroup[i], so);
1685 if (fputs(eol,so)==EOF)
1686 return 0;
1688 fclose(fi);
1690 return 1;
1693 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)
1695 char callerid[256];
1696 /* Prepare variables for substition in email body and subject */
1697 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1698 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1699 snprintf(passdata, passdatasize, "%d", msgnum);
1700 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1701 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1702 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1703 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1704 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1705 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1706 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1707 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1710 static char *quote(const char *from, char *to, size_t len)
1712 char *ptr = to;
1713 *ptr++ = '"';
1714 for (; ptr < to + len - 1; from++) {
1715 if (*from == '"')
1716 *ptr++ = '\\';
1717 else if (*from == '\0')
1718 break;
1719 *ptr++ = *from;
1721 if (ptr < to + len - 1)
1722 *ptr++ = '"';
1723 *ptr = '\0';
1724 return to;
1727 * fill in *tm for current time according to the proper timezone, if any.
1728 * Return tm so it can be used as a function argument.
1730 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1732 const struct vm_zone *z = NULL;
1733 time_t t = time(NULL);
1735 /* Does this user have a timezone specified? */
1736 if (!ast_strlen_zero(vmu->zonetag)) {
1737 /* Find the zone in the list */
1738 AST_LIST_LOCK(&zones);
1739 AST_LIST_TRAVERSE(&zones, z, list) {
1740 if (!strcmp(z->name, vmu->zonetag))
1741 break;
1743 AST_LIST_UNLOCK(&zones);
1745 ast_localtime(&t, tm, z ? z->timezone : NULL);
1746 return tm;
1749 /* same as mkstemp, but return a FILE * */
1750 static FILE *vm_mkftemp(char *template)
1752 FILE *p = NULL;
1753 int pfd = mkstemp(template);
1754 chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
1755 if (pfd > -1) {
1756 p = fdopen(pfd, "w+");
1757 if (!p) {
1758 close(pfd);
1759 pfd = -1;
1762 return p;
1765 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)
1767 char date[256];
1768 char host[MAXHOSTNAMELEN] = "";
1769 char who[256];
1770 char bound[256];
1771 char fname[256];
1772 char dur[256];
1773 char tmpcmd[256];
1774 struct tm tm;
1775 char *passdata2;
1776 size_t len_passdata;
1777 #ifdef IMAP_STORAGE
1778 #define ENDL "\r\n"
1779 #else
1780 #define ENDL "\n"
1781 #endif
1783 gethostname(host, sizeof(host) - 1);
1784 if (strchr(srcemail, '@'))
1785 ast_copy_string(who, srcemail, sizeof(who));
1786 else {
1787 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1789 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1790 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1791 fprintf(p, "Date: %s" ENDL, date);
1793 /* Set date format for voicemail mail */
1794 strftime(date, sizeof(date), emaildateformat, &tm);
1796 if (*fromstring) {
1797 struct ast_channel *ast;
1798 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1799 char *passdata;
1800 int vmlen = strlen(fromstring)*3 + 200;
1801 if ((passdata = alloca(vmlen))) {
1802 memset(passdata, 0, vmlen);
1803 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1804 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1805 len_passdata = strlen(passdata) * 2 + 3;
1806 passdata2 = alloca(len_passdata);
1807 fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
1808 } else
1809 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1810 ast_channel_free(ast);
1811 } else
1812 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1813 } else
1814 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
1815 len_passdata = strlen(vmu->fullname) * 2 + 3;
1816 passdata2 = alloca(len_passdata);
1817 fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
1818 if (emailsubject) {
1819 struct ast_channel *ast;
1820 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1821 char *passdata;
1822 int vmlen = strlen(emailsubject)*3 + 200;
1823 if ((passdata = alloca(vmlen))) {
1824 memset(passdata, 0, vmlen);
1825 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1826 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
1827 fprintf(p, "Subject: %s" ENDL, passdata);
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 if (*emailtitle) {
1834 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1835 fprintf(p, ENDL) ;
1836 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1837 fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
1838 else
1839 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
1840 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
1841 if(imap) {
1842 /* additional information needed for IMAP searching */
1843 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
1844 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
1845 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
1846 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
1847 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
1848 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
1849 fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
1850 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
1851 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
1852 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
1853 if (!ast_strlen_zero(category))
1854 fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
1855 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
1856 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
1858 if (!ast_strlen_zero(cidnum))
1859 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, cidnum);
1860 if (!ast_strlen_zero(cidname))
1861 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, cidname);
1862 fprintf(p, "MIME-Version: 1.0" ENDL);
1863 if (attach_user_voicemail) {
1864 /* Something unique. */
1865 snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
1867 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
1868 fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
1869 fprintf(p, "--%s" ENDL, bound);
1871 fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
1872 if (emailbody) {
1873 struct ast_channel *ast;
1874 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1875 char *passdata;
1876 int vmlen = strlen(emailbody)*3 + 200;
1877 if ((passdata = alloca(vmlen))) {
1878 memset(passdata, 0, vmlen);
1879 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1880 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
1881 fprintf(p, "%s" ENDL, passdata);
1882 } else
1883 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1884 ast_channel_free(ast);
1885 } else
1886 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1887 } else {
1888 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
1890 "in mailbox %s from %s, on %s so you might" ENDL
1891 "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname,
1892 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1894 if (attach_user_voicemail) {
1895 /* Eww. We want formats to tell us their own MIME type */
1896 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
1897 char tmpdir[256], newtmp[256];
1898 int tmpfd = -1;
1900 if (vmu->volgain < -.001 || vmu->volgain > .001) {
1901 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
1902 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
1903 tmpfd = mkstemp(newtmp);
1904 chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
1905 if (option_debug > 2)
1906 ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
1907 if (tmpfd > -1) {
1908 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
1909 ast_safe_system(tmpcmd);
1910 attach = newtmp;
1911 if (option_debug > 2)
1912 ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
1915 fprintf(p, "--%s" ENDL, bound);
1916 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
1917 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
1918 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
1919 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
1920 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1921 base_encode(fname, p);
1922 fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
1923 if (tmpfd > -1) {
1924 unlink(fname);
1925 close(tmpfd);
1926 unlink(newtmp);
1929 #undef ENDL
1931 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)
1933 FILE *p=NULL;
1934 char tmp[80] = "/tmp/astmail-XXXXXX";
1935 char tmp2[256];
1937 if (vmu && ast_strlen_zero(vmu->email)) {
1938 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
1939 return(0);
1941 if (!strcmp(format, "wav49"))
1942 format = "WAV";
1943 if (option_debug > 2)
1944 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));
1945 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1946 command hangs */
1947 if ((p = vm_mkftemp(tmp)) == NULL) {
1948 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1949 return -1;
1950 } else {
1951 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
1952 fclose(p);
1953 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1954 ast_safe_system(tmp2);
1955 if (option_debug > 2)
1956 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
1958 return 0;
1961 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)
1963 char date[256];
1964 char host[MAXHOSTNAMELEN] = "";
1965 char who[256];
1966 char dur[PATH_MAX];
1967 char tmp[80] = "/tmp/astmail-XXXXXX";
1968 char tmp2[PATH_MAX];
1969 struct tm tm;
1970 FILE *p;
1972 if ((p = vm_mkftemp(tmp)) == NULL) {
1973 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1974 return -1;
1975 } else {
1976 gethostname(host, sizeof(host)-1);
1977 if (strchr(srcemail, '@'))
1978 ast_copy_string(who, srcemail, sizeof(who));
1979 else {
1980 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1982 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1983 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1984 fprintf(p, "Date: %s\n", date);
1986 if (*pagerfromstring) {
1987 struct ast_channel *ast;
1988 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1989 char *passdata;
1990 int vmlen = strlen(fromstring)*3 + 200;
1991 if ((passdata = alloca(vmlen))) {
1992 memset(passdata, 0, vmlen);
1993 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1994 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
1995 fprintf(p, "From: %s <%s>\n", passdata, who);
1996 } else
1997 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1998 ast_channel_free(ast);
1999 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2000 } else
2001 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2002 fprintf(p, "To: %s\n", pager);
2003 if (pagersubject) {
2004 struct ast_channel *ast;
2005 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2006 char *passdata;
2007 int vmlen = strlen(pagersubject) * 3 + 200;
2008 if ((passdata = alloca(vmlen))) {
2009 memset(passdata, 0, vmlen);
2010 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2011 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2012 fprintf(p, "Subject: %s\n\n", passdata);
2013 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2014 ast_channel_free(ast);
2015 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2016 } else
2017 fprintf(p, "Subject: New VM\n\n");
2018 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2019 if (pagerbody) {
2020 struct ast_channel *ast;
2021 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2022 char *passdata;
2023 int vmlen = strlen(pagerbody)*3 + 200;
2024 if ((passdata = alloca(vmlen))) {
2025 memset(passdata, 0, vmlen);
2026 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2027 pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2028 fprintf(p, "%s\n", passdata);
2029 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2030 ast_channel_free(ast);
2031 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2032 } else {
2033 fprintf(p, "New %s long msg in box %s\n"
2034 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2036 fclose(p);
2037 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2038 ast_safe_system(tmp2);
2039 if (option_debug > 2)
2040 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
2042 return 0;
2045 static int get_date(char *s, int len)
2047 struct tm tm;
2048 time_t t;
2050 time(&t);
2052 ast_localtime(&t, &tm, NULL);
2054 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2057 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2059 int res;
2060 char fn[PATH_MAX];
2061 char dest[PATH_MAX];
2063 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2065 if ((res = create_dirpath(dest, sizeof(dest), context, ext, "greet"))) {
2066 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
2067 return -1;
2070 RETRIEVE(fn, -1);
2071 if (ast_fileexists(fn, NULL, NULL) > 0) {
2072 res = ast_stream_and_wait(chan, fn, chan->language, ecodes);
2073 if (res) {
2074 DISPOSE(fn, -1);
2075 return res;
2077 } else {
2078 /* Dispose just in case */
2079 DISPOSE(fn, -1);
2080 res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
2081 if (res)
2082 return res;
2083 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2084 if (res)
2085 return res;
2087 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
2088 return res;
2091 static void free_user(struct ast_vm_user *vmu)
2093 if (ast_test_flag(vmu, VM_ALLOCED))
2094 free(vmu);
2097 static void free_zone(struct vm_zone *z)
2099 free(z);
2102 static const char *mbox(int id)
2104 static const char *msgs[] = {
2105 "INBOX",
2106 "Old",
2107 "Work",
2108 "Family",
2109 "Friends",
2110 "Cust1",
2111 "Cust2",
2112 "Cust3",
2113 "Cust4",
2114 "Cust5",
2116 return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2118 #ifdef IMAP_STORAGE
2119 static int folder_int(const char *folder)
2121 /*assume a NULL folder means INBOX*/
2122 if (!folder)
2123 return 0;
2124 if(!strcasecmp(folder, "INBOX"))
2125 return 0;
2126 else if (!strcasecmp(folder, "Old"))
2127 return 1;
2128 else if (!strcasecmp(folder, "Work"))
2129 return 2;
2130 else if (!strcasecmp(folder, "Family"))
2131 return 3;
2132 else if (!strcasecmp(folder, "Friends"))
2133 return 4;
2134 else if (!strcasecmp(folder, "Cust1"))
2135 return 5;
2136 else if (!strcasecmp(folder, "Cust2"))
2137 return 6;
2138 else if (!strcasecmp(folder, "Cust3"))
2139 return 7;
2140 else if (!strcasecmp(folder, "Cust4"))
2141 return 8;
2142 else if (!strcasecmp(folder, "Cust5"))
2143 return 9;
2144 else /*assume they meant INBOX if folder is not found otherwise*/
2145 return 0;
2147 #endif
2149 #ifdef ODBC_STORAGE
2150 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
2151 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2153 int x = -1;
2154 int res;
2155 SQLHSTMT stmt;
2156 char sql[PATH_MAX];
2157 char rowdata[20];
2158 char tmp[PATH_MAX] = "";
2159 struct odbc_obj *obj;
2160 char *context;
2161 struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2163 if (newmsgs)
2164 *newmsgs = 0;
2165 if (oldmsgs)
2166 *oldmsgs = 0;
2168 /* If no mailbox, return immediately */
2169 if (ast_strlen_zero(mailbox))
2170 return 0;
2172 ast_copy_string(tmp, mailbox, sizeof(tmp));
2174 context = strchr(tmp, '@');
2175 if (context) {
2176 *context = '\0';
2177 context++;
2178 } else
2179 context = "default";
2181 obj = ast_odbc_request_obj(odbc_database, 0);
2182 if (obj) {
2183 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2184 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2185 if (!stmt) {
2186 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2187 ast_odbc_release_obj(obj);
2188 goto yuck;
2190 res = SQLFetch(stmt);
2191 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2192 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2193 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2194 ast_odbc_release_obj(obj);
2195 goto yuck;
2197 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2198 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2199 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2200 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2201 ast_odbc_release_obj(obj);
2202 goto yuck;
2204 *newmsgs = atoi(rowdata);
2205 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2207 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2208 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2209 if (!stmt) {
2210 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2211 ast_odbc_release_obj(obj);
2212 goto yuck;
2214 res = SQLFetch(stmt);
2215 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2216 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2217 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2218 ast_odbc_release_obj(obj);
2219 goto yuck;
2221 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2222 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2223 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2224 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2225 ast_odbc_release_obj(obj);
2226 goto yuck;
2228 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2229 ast_odbc_release_obj(obj);
2230 *oldmsgs = atoi(rowdata);
2231 x = 0;
2232 } else
2233 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2235 yuck:
2236 return x;
2239 static int messagecount(const char *context, const char *mailbox, const char *folder)
2241 struct odbc_obj *obj = NULL;
2242 int nummsgs = 0;
2243 int res;
2244 SQLHSTMT stmt = NULL;
2245 char sql[PATH_MAX];
2246 char rowdata[20];
2247 struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2248 if (!folder)
2249 folder = "INBOX";
2250 /* If no mailbox, return immediately */
2251 if (ast_strlen_zero(mailbox))
2252 return 0;
2254 obj = ast_odbc_request_obj(odbc_database, 0);
2255 if (obj) {
2256 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2257 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2258 if (!stmt) {
2259 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2260 goto yuck;
2262 res = SQLFetch(stmt);
2263 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2264 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2265 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2266 goto yuck;
2268 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2269 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2270 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2271 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2272 goto yuck;
2274 nummsgs = atoi(rowdata);
2275 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2276 } else
2277 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2279 yuck:
2280 if (obj)
2281 ast_odbc_release_obj(obj);
2282 return nummsgs;
2285 static int has_voicemail(const char *mailbox, const char *folder)
2287 char tmp[256], *tmp2 = tmp, *mbox, *context;
2288 ast_copy_string(tmp, mailbox, sizeof(tmp));
2289 while ((context = mbox = strsep(&tmp2, ","))) {
2290 strsep(&context, "@");
2291 if (ast_strlen_zero(context))
2292 context = "default";
2293 if (messagecount(context, mbox, folder))
2294 return 1;
2296 return 0;
2299 #elif defined(IMAP_STORAGE)
2301 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)
2303 char *myserveremail = serveremail;
2304 char fn[PATH_MAX];
2305 char mailbox[256];
2306 char *stringp;
2307 FILE *p=NULL;
2308 char tmp[80] = "/tmp/astmail-XXXXXX";
2309 long len;
2310 void *buf;
2311 STRING str;
2313 /* Attach only the first format */
2314 fmt = ast_strdupa(fmt);
2315 stringp = fmt;
2316 strsep(&stringp, "|");
2318 if (!ast_strlen_zero(vmu->serveremail))
2319 myserveremail = vmu->serveremail;
2321 make_file(fn, sizeof(fn), dir, msgnum);
2323 if (ast_strlen_zero(vmu->email))
2324 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2326 if (!strcmp(fmt, "wav49"))
2327 fmt = "WAV";
2328 if(option_debug > 2)
2329 ast_log(LOG_DEBUG, "Storing file '%s', format '%s'\n", fn, fmt);
2330 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2331 command hangs */
2332 if ((p = vm_mkftemp(tmp)) == NULL) {
2333 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2334 return -1;
2335 } else {
2336 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);
2337 /* read mail file to memory */
2338 len = ftell(p);
2339 rewind(p);
2340 if((buf = ast_malloc(len+1)) == NIL) {
2341 ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2342 return -1;
2344 fread(buf, len, 1, p);
2345 ((char *)buf)[len] = '\0';
2346 INIT(&str, mail_string, buf, len);
2347 init_mailstream(vms, 0);
2348 imap_mailbox_name(mailbox, sizeof(mailbox), vms, 0, 1);
2349 if(!mail_append(vms->mailstream, mailbox, &str))
2350 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2351 fclose(p);
2352 unlink(tmp);
2353 ast_free(buf);
2354 if(option_debug > 2)
2355 ast_log(LOG_DEBUG, "%s stored\n", fn);
2357 return 0;
2361 static int messagecount(const char *context, const char *mailbox, const char *folder)
2363 SEARCHPGM *pgm;
2364 SEARCHHEADER *hdr;
2366 struct ast_vm_user *vmu, vmus;
2367 struct vm_state *vms_p;
2368 int ret = 0;
2369 int fold = folder_int(folder);
2371 if (ast_strlen_zero(mailbox))
2372 return 0;
2374 /* We have to get the user before we can open the stream! */
2375 /* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2376 vmu = find_user(&vmus, context, mailbox);
2377 if (!vmu) {
2378 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailbox,context);
2379 return -1;
2380 } else {
2381 /* No IMAP account available */
2382 if (vmu->imapuser[0] == '\0') {
2383 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2384 return -1;
2388 /* check if someone is accessing this box right now... */
2389 vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
2390 if (!vms_p) {
2391 vms_p = get_vm_state_by_mailbox(mailbox,1);
2393 if (vms_p) {
2394 if(option_debug > 2)
2395 ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
2396 if(fold == 0) {/*INBOX*/
2397 return vms_p->newmessages;
2399 if(fold == 1) {/*Old messages*/
2400 return vms_p->oldmessages;
2404 /* add one if not there... */
2405 vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
2406 if (!vms_p) {
2407 vms_p = get_vm_state_by_mailbox(mailbox,0);
2410 if (!vms_p) {
2411 if(option_debug > 2)
2412 ast_log (LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2413 if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
2414 return -1;
2416 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2417 ast_copy_string(vms_p->username, mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
2418 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2419 if(option_debug > 2)
2420 ast_log (LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2421 vms_p->updated = 1;
2422 /* set mailbox to INBOX! */
2423 ast_copy_string(vms_p->curbox, mbox(fold), sizeof(vms_p->curbox));
2424 init_vm_state(vms_p);
2425 vmstate_insert(vms_p);
2427 ret = init_mailstream(vms_p, fold);
2428 if (!vms_p->mailstream) {
2429 ast_log (LOG_ERROR,"IMAP mailstream is NULL\n");
2430 return -1;
2432 if (ret == 0) {
2433 pgm = mail_newsearchpgm ();
2434 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
2435 pgm->header = hdr;
2436 if (fold != 1) {
2437 pgm->unseen = 1;
2438 pgm->seen = 0;
2440 /* In the special case where fold is 1 (old messages) we have to do things a bit
2441 * differently. Old messages are stored in the INBOX but are marked as "seen"
2443 else {
2444 pgm->unseen = 0;
2445 pgm->seen = 1;
2447 pgm->undeleted = 1;
2448 pgm->deleted = 0;
2450 vms_p->vmArrayIndex = 0;
2451 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2452 if(fold == 0)
2453 vms_p->newmessages = vms_p->vmArrayIndex;
2454 if(fold == 1)
2455 vms_p->oldmessages = vms_p->vmArrayIndex;
2456 /*Freeing the searchpgm also frees the searchhdr*/
2457 mail_free_searchpgm(&pgm);
2458 vms_p->updated = 0;
2459 return vms_p->vmArrayIndex;
2460 } else {
2461 mail_ping(vms_p->mailstream);
2463 return 0;
2465 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
2467 char tmp[PATH_MAX] = "";
2468 char *mailboxnc;
2469 char *context;
2470 char *mb;
2471 char *cur;
2472 if (newmsgs)
2473 *newmsgs = 0;
2474 if (oldmsgs)
2475 *oldmsgs = 0;
2477 if(option_debug > 2)
2478 ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox_context);
2479 /* If no mailbox, return immediately */
2480 if (ast_strlen_zero(mailbox_context))
2481 return 0;
2483 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2484 context = strchr(tmp, '@');
2485 if (strchr(mailbox_context, ',')) {
2486 int tmpnew, tmpold;
2487 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2488 mb = tmp;
2489 while((cur = strsep(&mb, ", "))) {
2490 if (!ast_strlen_zero(cur)) {
2491 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2492 return -1;
2493 else {
2494 if (newmsgs)
2495 *newmsgs += tmpnew;
2496 if (oldmsgs)
2497 *oldmsgs += tmpold;
2501 return 0;
2503 if (context) {
2504 *context = '\0';
2505 mailboxnc = tmp;
2506 context++;
2507 } else {
2508 context = "default";
2509 mailboxnc = (char *)mailbox_context;
2511 if (newmsgs) {
2512 if((*newmsgs = messagecount(context, mailboxnc, "INBOX")) < 0)
2513 return -1;
2515 if (oldmsgs) {
2516 if((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
2517 return -1;
2519 return 0;
2523 static int has_voicemail(const char *mailbox, const char *folder)
2525 char tmp[256], *tmp2, *mbox, *context;
2526 ast_copy_string(tmp, mailbox, sizeof(tmp));
2527 tmp2 = tmp;
2528 if(strchr(tmp2, ',')) {
2529 while((mbox = strsep(&tmp2, ","))) {
2530 if(!ast_strlen_zero(mbox)) {
2531 if (has_voicemail(mbox, folder))
2532 return 1;
2536 if ((context= strchr(tmp, '@')))
2537 *context++ = '\0';
2538 else
2539 context = "default";
2540 return messagecount(context, tmp, folder) ? 1 : 0;
2543 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)
2545 char dest[256];
2546 struct vm_state *sendvms = NULL, *destvms = NULL;
2547 char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2548 if(!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 2)))
2550 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
2551 return -1;
2553 if(!(destvms = get_vm_state_by_imapuser(recip->imapuser, 2)))
2555 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
2556 return -1;
2558 imap_mailbox_name(dest, sizeof(dest), destvms, imbox, 1);
2559 snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
2560 if((mail_copy(sendvms->mailstream, messagestring, dest) == T))
2561 return 0;
2562 ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
2563 return -1;
2566 #endif
2567 #ifndef IMAP_STORAGE
2568 /* copy message only used by file storage */
2569 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)
2571 char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
2572 const char *frombox = mbox(imbox);
2573 int recipmsgnum;
2575 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2577 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2579 if (!dir)
2580 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2581 else
2582 ast_copy_string(fromdir, dir, sizeof(fromdir));
2584 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2586 if (vm_lock_path(todir))
2587 return ERROR_LOCK_PATH;
2589 recipmsgnum = 0;
2590 do {
2591 make_file(topath, sizeof(topath), todir, recipmsgnum);
2592 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
2593 break;
2594 recipmsgnum++;
2595 } while (recipmsgnum < recip->maxmsg);
2596 if (recipmsgnum < recip->maxmsg) {
2597 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2598 } else {
2599 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2601 ast_unlock_path(todir);
2602 notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2604 return 0;
2606 #endif
2607 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2608 static int messagecount(const char *context, const char *mailbox, const char *folder)
2610 return __has_voicemail(context, mailbox, folder, 0);
2614 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2616 DIR *dir;
2617 struct dirent *de;
2618 char fn[256];
2619 int ret = 0;
2620 if (!folder)
2621 folder = "INBOX";
2622 /* If no mailbox, return immediately */
2623 if (ast_strlen_zero(mailbox))
2624 return 0;
2625 if (!context)
2626 context = "default";
2627 snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2628 dir = opendir(fn);
2629 if (!dir)
2630 return 0;
2631 while ((de = readdir(dir))) {
2632 if (!strncasecmp(de->d_name, "msg", 3)) {
2633 if (shortcircuit) {
2634 ret = 1;
2635 break;
2636 } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2637 ret++;
2640 closedir(dir);
2641 return ret;
2645 static int has_voicemail(const char *mailbox, const char *folder)
2647 char tmp[256], *tmp2 = tmp, *mbox, *context;
2648 ast_copy_string(tmp, mailbox, sizeof(tmp));
2649 while ((mbox = strsep(&tmp2, ","))) {
2650 if ((context = strchr(mbox, '@')))
2651 *context++ = '\0';
2652 else
2653 context = "default";
2654 if (__has_voicemail(context, mbox, folder, 1))
2655 return 1;
2657 return 0;
2661 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2663 char tmp[256];
2664 char *context;
2666 if (newmsgs)
2667 *newmsgs = 0;
2668 if (oldmsgs)
2669 *oldmsgs = 0;
2670 /* If no mailbox, return immediately */
2671 if (ast_strlen_zero(mailbox))
2672 return 0;
2673 if (strchr(mailbox, ',')) {
2674 int tmpnew, tmpold;
2675 char *mb, *cur;
2677 ast_copy_string(tmp, mailbox, sizeof(tmp));
2678 mb = tmp;
2679 while ((cur = strsep(&mb, ", "))) {
2680 if (!ast_strlen_zero(cur)) {
2681 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2682 return -1;
2683 else {
2684 if (newmsgs)
2685 *newmsgs += tmpnew;
2686 if (oldmsgs)
2687 *oldmsgs += tmpold;
2691 return 0;
2693 ast_copy_string(tmp, mailbox, sizeof(tmp));
2694 context = strchr(tmp, '@');
2695 if (context) {
2696 *context = '\0';
2697 context++;
2698 } else
2699 context = "default";
2700 if (newmsgs)
2701 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2702 if (oldmsgs)
2703 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2704 return 0;
2707 #endif
2709 static void run_externnotify(char *context, char *extension)
2711 char arguments[255];
2712 char ext_context[256] = "";
2713 int newvoicemails = 0, oldvoicemails = 0;
2714 struct ast_smdi_mwi_message *mwi_msg;
2716 if (!ast_strlen_zero(context))
2717 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2718 else
2719 ast_copy_string(ext_context, extension, sizeof(ext_context));
2721 if (!strcasecmp(externnotify, "smdi")) {
2722 if (ast_app_has_voicemail(ext_context, NULL))
2723 ast_smdi_mwi_set(smdi_iface, extension);
2724 else
2725 ast_smdi_mwi_unset(smdi_iface, extension);
2727 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2728 ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2729 if (!strncmp(mwi_msg->cause, "INV", 3))
2730 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2731 else if (!strncmp(mwi_msg->cause, "BLK", 3))
2732 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2733 ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2734 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2735 } else {
2736 if (option_debug)
2737 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2739 } else if (!ast_strlen_zero(externnotify)) {
2740 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2741 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2742 } else {
2743 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2744 if (option_debug)
2745 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2746 ast_safe_system(arguments);
2751 struct leave_vm_options {
2752 unsigned int flags;
2753 signed char record_gain;
2756 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2758 #ifdef IMAP_STORAGE
2759 int newmsgs, oldmsgs;
2760 struct vm_state *vms = NULL;
2761 #endif
2762 char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
2763 char callerid[256];
2764 FILE *txt;
2765 char date[256];
2766 int txtdes;
2767 int res = 0;
2768 int msgnum;
2769 int duration = 0;
2770 int ausemacro = 0;
2771 int ousemacro = 0;
2772 int ouseexten = 0;
2773 char dir[PATH_MAX], tmpdir[PATH_MAX];
2774 char dest[PATH_MAX];
2775 char fn[PATH_MAX];
2776 char prefile[PATH_MAX] = "";
2777 char tempfile[PATH_MAX] = "";
2778 char ext_context[256] = "";
2779 char fmt[80];
2780 char *context;
2781 char ecodes[16] = "#";
2782 char tmp[1024] = "", *tmpptr;
2783 struct ast_vm_user *vmu;
2784 struct ast_vm_user svm;
2785 const char *category = NULL;
2787 ast_copy_string(tmp, ext, sizeof(tmp));
2788 ext = tmp;
2789 context = strchr(tmp, '@');
2790 if (context) {
2791 *context++ = '\0';
2792 tmpptr = strchr(context, '&');
2793 } else {
2794 tmpptr = strchr(ext, '&');
2797 if (tmpptr)
2798 *tmpptr++ = '\0';
2800 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2802 if(option_debug > 2)
2803 ast_log(LOG_DEBUG, "Before find_user\n");
2804 if (!(vmu = find_user(&svm, context, ext))) {
2805 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2806 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2807 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2808 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2809 return res;
2811 /* Setup pre-file if appropriate */
2812 if (strcmp(vmu->context, "default"))
2813 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2814 else
2815 ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
2816 if (ast_test_flag(options, OPT_BUSY_GREETING)) {
2817 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
2818 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2819 } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
2820 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
2821 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2823 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2824 if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
2825 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
2826 return -1;
2828 RETRIEVE(tempfile, -1);
2829 if (ast_fileexists(tempfile, NULL, NULL) > 0)
2830 ast_copy_string(prefile, tempfile, sizeof(prefile));
2831 DISPOSE(tempfile, -1);
2832 /* It's easier just to try to make it than to check for its existence */
2833 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2834 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
2836 /* Check current or macro-calling context for special extensions */
2837 if (ast_test_flag(vmu, VM_OPERATOR)) {
2838 if (!ast_strlen_zero(vmu->exit)) {
2839 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
2840 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2841 ouseexten = 1;
2843 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
2844 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2845 ouseexten = 1;
2847 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2848 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2849 ousemacro = 1;
2853 if (!ast_strlen_zero(vmu->exit)) {
2854 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2855 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2856 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2857 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2858 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2859 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2860 ausemacro = 1;
2863 /* Play the beginning intro if desired */
2864 if (!ast_strlen_zero(prefile)) {
2865 #ifdef ODBC_STORAGE
2866 int success =
2867 #endif
2868 RETRIEVE(prefile, -1);
2869 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2870 if (ast_streamfile(chan, prefile, chan->language) > -1)
2871 res = ast_waitstream(chan, ecodes);
2872 #ifdef ODBC_STORAGE
2873 if (success == -1) {
2874 /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
2875 if (option_debug)
2876 ast_log(LOG_DEBUG, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
2877 store_file(prefile, vmu->mailbox, vmu->context, -1);
2879 #endif
2880 } else {
2881 if (option_debug)
2882 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2883 res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2885 DISPOSE(prefile, -1);
2886 if (res < 0) {
2887 if (option_debug)
2888 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2889 free_user(vmu);
2890 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2891 return -1;
2894 if (res == '#') {
2895 /* On a '#' we skip the instructions */
2896 ast_set_flag(options, OPT_SILENT);
2897 res = 0;
2899 if (!res && !ast_test_flag(options, OPT_SILENT)) {
2900 res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
2901 if (res == '#') {
2902 ast_set_flag(options, OPT_SILENT);
2903 res = 0;
2906 if (res > 0)
2907 ast_stopstream(chan);
2908 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2909 other than the operator -- an automated attendant or mailbox login for example */
2910 if (res == '*') {
2911 chan->exten[0] = 'a';
2912 chan->exten[1] = '\0';
2913 if (!ast_strlen_zero(vmu->exit)) {
2914 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2915 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2916 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2918 chan->priority = 0;
2919 free_user(vmu);
2920 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2921 return 0;
2924 /* Check for a '0' here */
2925 if (res == '0') {
2926 transfer:
2927 if (ouseexten || ousemacro) {
2928 chan->exten[0] = 'o';
2929 chan->exten[1] = '\0';
2930 if (!ast_strlen_zero(vmu->exit)) {
2931 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2932 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2933 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2935 ast_play_and_wait(chan, "transfer");
2936 chan->priority = 0;
2937 free_user(vmu);
2938 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2940 return 0;
2942 if (res < 0) {
2943 free_user(vmu);
2944 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2945 return -1;
2947 /* The meat of recording the message... All the announcements and beeps have been played*/
2948 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2949 if (!ast_strlen_zero(fmt)) {
2950 msgnum = 0;
2952 #ifdef IMAP_STORAGE
2953 /* Is ext a mailbox? */
2954 /* must open stream for this user to get info! */
2955 res = inboxcount(ext_context, &newmsgs, &oldmsgs);
2956 if(res < 0) {
2957 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
2958 return -1;
2960 if(!(vms = get_vm_state_by_mailbox(ext,0))) {
2961 /*It is possible under certain circumstances that inboxcount did not create a vm_state when it was needed. This is a catchall which will
2962 * rarely be used*/
2963 if (!(vms = ast_calloc(1, sizeof(*vms)))) {
2964 ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
2965 return -1;
2967 ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
2968 ast_copy_string(vms->username, ext, sizeof(vms->username));
2969 vms->mailstream = NIL;
2970 if (option_debug > 2)
2971 ast_log(LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms->imapuser);
2972 vms->updated=1;
2973 ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
2974 init_vm_state(vms);
2975 vmstate_insert(vms);
2976 vms = get_vm_state_by_mailbox(ext,0);
2978 vms->newmessages++;
2979 /* here is a big difference! We add one to it later */
2980 msgnum = newmsgs + oldmsgs;
2981 if (option_debug > 2)
2982 ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
2983 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
2984 /* set variable for compatability */
2985 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
2987 /* Check if mailbox is full */
2988 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
2989 if(option_debug)
2990 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
2991 ast_play_and_wait(chan, "vm-mailboxfull");
2992 return -1;
2994 /* here is a big difference! We add one to it later */
2995 if (option_debug > 2)
2996 ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
2997 #else
2998 if (count_messages(vmu, dir) >= vmu->maxmsg) {
2999 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3000 if (!res)
3001 res = ast_waitstream(chan, "");
3002 ast_log(LOG_WARNING, "No more messages possible\n");
3003 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3004 goto leave_vm_out;
3007 #endif
3008 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
3009 txtdes = mkstemp(tmptxtfile);
3010 chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
3011 if (txtdes < 0) {
3012 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3013 if (!res)
3014 res = ast_waitstream(chan, "");
3015 ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
3016 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3017 goto leave_vm_out;
3020 /* Now play the beep once we have the message number for our next message. */
3021 if (res >= 0) {
3022 /* Unless we're *really* silent, try to send the beep */
3023 res = ast_stream_and_wait(chan, "beep", chan->language, "");
3026 /* Store information */
3027 txt = fdopen(txtdes, "w+");
3028 if (txt) {
3029 get_date(date, sizeof(date));
3030 fprintf(txt,
3031 ";\n"
3032 "; Message Information file\n"
3033 ";\n"
3034 "[message]\n"
3035 "origmailbox=%s\n"
3036 "context=%s\n"
3037 "macrocontext=%s\n"
3038 "exten=%s\n"
3039 "priority=%d\n"
3040 "callerchan=%s\n"
3041 "callerid=%s\n"
3042 "origdate=%s\n"
3043 "origtime=%ld\n"
3044 "category=%s\n",
3045 ext,
3046 chan->context,
3047 chan->macrocontext,
3048 chan->exten,
3049 chan->priority,
3050 chan->name,
3051 ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
3052 date, (long)time(NULL),
3053 category ? category : "");
3054 } else
3055 ast_log(LOG_WARNING, "Error opening text file for output\n");
3056 #ifdef IMAP_STORAGE
3057 res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
3058 #else
3059 res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL);
3060 #endif
3062 if (txt) {
3063 if (duration < vmminmessage) {
3064 fclose(txt);
3065 if (option_verbose > 2)
3066 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
3067 ast_filedelete(tmptxtfile, NULL);
3068 unlink(tmptxtfile);
3069 } else {
3070 fprintf(txt, "duration=%d\n", duration);
3071 fclose(txt);
3072 if (vm_lock_path(dir)) {
3073 ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
3074 /* Delete files */
3075 ast_filedelete(tmptxtfile, NULL);
3076 unlink(tmptxtfile);
3077 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
3078 if (option_debug)
3079 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
3080 unlink(tmptxtfile);
3081 ast_unlock_path(dir);
3082 } else {
3083 for (;;) {
3084 make_file(fn, sizeof(fn), dir, msgnum);
3085 if (!EXISTS(dir, msgnum, fn, NULL))
3086 break;
3087 msgnum++;
3090 /* assign a variable with the name of the voicemail file */
3091 #ifndef IMAP_STORAGE
3092 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3093 #else
3094 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3095 #endif
3097 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3098 ast_filerename(tmptxtfile, fn, NULL);
3099 rename(tmptxtfile, txtfile);
3101 ast_unlock_path(dir);
3102 /* We must store the file first, before copying the message, because
3103 * ODBC storage does the entire copy with SQL.
3105 if (ast_fileexists(fn, NULL, NULL) > 0) {
3106 STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
3109 /* Are there to be more recipients of this message? */
3110 while (tmpptr) {
3111 struct ast_vm_user recipu, *recip;
3112 char *exten, *context;
3114 exten = strsep(&tmpptr, "&");
3115 context = strchr(exten, '@');
3116 if (context) {
3117 *context = '\0';
3118 context++;
3120 if ((recip = find_user(&recipu, context, exten))) {
3121 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
3122 free_user(recip);
3125 /* Notification and disposal needs to happen after the copy, though. */
3126 if (ast_fileexists(fn, NULL, NULL)) {
3127 notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
3128 DISPOSE(dir, msgnum);
3133 if (res == '0') {
3134 goto transfer;
3135 } else if (res > 0)
3136 res = 0;
3138 if (duration < vmminmessage)
3139 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3140 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3141 else
3142 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3143 } else
3144 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3145 leave_vm_out:
3146 free_user(vmu);
3148 return res;
3151 #ifndef IMAP_STORAGE
3152 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3154 /* we know max messages, so stop process when number is hit */
3156 int x,dest;
3157 char sfn[PATH_MAX];
3158 char dfn[PATH_MAX];
3160 if (vm_lock_path(dir))
3161 return ERROR_LOCK_PATH;
3163 for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3164 make_file(sfn, sizeof(sfn), dir, x);
3165 if (EXISTS(dir, x, sfn, NULL)) {
3167 if (x != dest) {
3168 make_file(dfn, sizeof(dfn), dir, dest);
3169 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3172 dest++;
3175 ast_unlock_path(dir);
3177 return 0;
3179 #endif
3181 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3183 int d;
3184 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
3185 return d;
3188 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3190 #ifdef IMAP_STORAGE
3191 /* we must use mbox(x) folder names, and copy the message there */
3192 /* simple. huh? */
3193 char dbox[256];
3194 long res;
3195 char sequence[10];
3197 /* if save to Old folder, just leave in INBOX */
3198 if (box == 1) return 10;
3199 /* get the real IMAP message number for this message */
3200 snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
3201 imap_mailbox_name(dbox, sizeof(dbox), vms, box, 1);
3202 if(option_debug > 2)
3203 ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,dbox);
3204 res = mail_copy(vms->mailstream,sequence,dbox);
3205 if (res == 1) return 0;
3206 return 1;
3207 #else
3208 char *dir = vms->curdir;
3209 char *username = vms->username;
3210 char *context = vmu->context;
3211 char sfn[PATH_MAX];
3212 char dfn[PATH_MAX];
3213 char ddir[PATH_MAX];
3214 const char *dbox = mbox(box);
3215 int x;
3216 make_file(sfn, sizeof(sfn), dir, msg);
3217 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
3219 if (vm_lock_path(ddir))
3220 return ERROR_LOCK_PATH;
3222 for (x = 0; x < vmu->maxmsg; x++) {
3223 make_file(dfn, sizeof(dfn), ddir, x);
3224 if (!EXISTS(ddir, x, dfn, NULL))
3225 break;
3227 if (x >= vmu->maxmsg) {
3228 ast_unlock_path(ddir);
3229 return -1;
3231 if (strcmp(sfn, dfn)) {
3232 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
3234 ast_unlock_path(ddir);
3235 #endif
3236 return 0;
3239 static int adsi_logo(unsigned char *buf)
3241 int bytes = 0;
3242 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
3243 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
3244 return bytes;
3247 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
3249 unsigned char buf[256];
3250 int bytes=0;
3251 int x;
3252 char num[5];
3254 *useadsi = 0;
3255 bytes += ast_adsi_data_mode(buf + bytes);
3256 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3258 bytes = 0;
3259 bytes += adsi_logo(buf);
3260 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3261 #ifdef DISPLAY
3262 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
3263 #endif
3264 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3265 bytes += ast_adsi_data_mode(buf + bytes);
3266 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3268 if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
3269 bytes = 0;
3270 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
3271 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3272 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3273 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3274 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3275 return 0;
3278 #ifdef DISPLAY
3279 /* Add a dot */
3280 bytes = 0;
3281 bytes += ast_adsi_logo(buf);
3282 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3283 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
3284 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3285 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3286 #endif
3287 bytes = 0;
3288 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
3289 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
3290 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
3291 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
3292 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
3293 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
3294 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3296 #ifdef DISPLAY
3297 /* Add another dot */
3298 bytes = 0;
3299 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
3300 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3302 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3303 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3304 #endif
3306 bytes = 0;
3307 /* These buttons we load but don't use yet */
3308 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
3309 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
3310 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
3311 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
3312 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
3313 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
3314 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3316 #ifdef DISPLAY
3317 /* Add another dot */
3318 bytes = 0;
3319 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
3320 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3321 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3322 #endif
3324 bytes = 0;
3325 for (x=0;x<5;x++) {
3326 snprintf(num, sizeof(num), "%d", x);
3327 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
3329 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
3330 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3332 #ifdef DISPLAY
3333 /* Add another dot */
3334 bytes = 0;
3335 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
3336 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3337 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3338 #endif
3340 if (ast_adsi_end_download(chan)) {
3341 bytes = 0;
3342 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
3343 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3344 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3345 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3346 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3347 return 0;
3349 bytes = 0;
3350 bytes += ast_adsi_download_disconnect(buf + bytes);
3351 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3352 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3354 if (option_debug)
3355 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
3357 #ifdef DISPLAY
3358 /* Add last dot */
3359 bytes = 0;
3360 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
3361 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3362 #endif
3363 if (option_debug)
3364 ast_log(LOG_DEBUG, "Restarting session...\n");
3366 bytes = 0;
3367 /* Load the session now */
3368 if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
3369 *useadsi = 1;
3370 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
3371 } else
3372 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
3374 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3375 return 0;
3378 static void adsi_begin(struct ast_channel *chan, int *useadsi)
3380 int x;
3381 if (!ast_adsi_available(chan))
3382 return;
3383 x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
3384 if (x < 0)
3385 return;
3386 if (!x) {
3387 if (adsi_load_vmail(chan, useadsi)) {
3388 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
3389 return;
3391 } else
3392 *useadsi = 1;
3395 static void adsi_login(struct ast_channel *chan)
3397 unsigned char buf[256];
3398 int bytes=0;
3399 unsigned char keys[8];
3400 int x;
3401 if (!ast_adsi_available(chan))
3402 return;
3404 for (x=0;x<8;x++)
3405 keys[x] = 0;
3406 /* Set one key for next */
3407 keys[3] = ADSI_KEY_APPS + 3;
3409 bytes += adsi_logo(buf + bytes);
3410 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
3411 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
3412 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3413 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
3414 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
3415 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
3416 bytes += ast_adsi_set_keys(buf + bytes, keys);
3417 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3418 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3421 static void adsi_password(struct ast_channel *chan)
3423 unsigned char buf[256];
3424 int bytes=0;
3425 unsigned char keys[8];
3426 int x;
3427 if (!ast_adsi_available(chan))
3428 return;
3430 for (x=0;x<8;x++)
3431 keys[x] = 0;
3432 /* Set one key for next */
3433 keys[3] = ADSI_KEY_APPS + 3;
3435 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3436 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
3437 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
3438 bytes += ast_adsi_set_keys(buf + bytes, keys);
3439 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3440 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3443 static void adsi_folders(struct ast_channel *chan, int start, char *label)
3445 unsigned char buf[256];
3446 int bytes=0;
3447 unsigned char keys[8];
3448 int x,y;
3450 if (!ast_adsi_available(chan))
3451 return;
3453 for (x=0;x<5;x++) {
3454 y = ADSI_KEY_APPS + 12 + start + x;
3455 if (y > ADSI_KEY_APPS + 12 + 4)
3456 y = 0;
3457 keys[x] = ADSI_KEY_SKT | y;
3459 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
3460 keys[6] = 0;
3461 keys[7] = 0;
3463 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
3464 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
3465 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3466 bytes += ast_adsi_set_keys(buf + bytes, keys);
3467 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3469 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3472 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
3474 int bytes=0;
3475 unsigned char buf[256];
3476 char buf1[256], buf2[256];
3477 char fn2[PATH_MAX];
3479 char cid[256]="";
3480 char *val;
3481 char *name, *num;
3482 char datetime[21]="";
3483 FILE *f;
3485 unsigned char keys[8];
3487 int x;
3489 if (!ast_adsi_available(chan))
3490 return;
3492 /* Retrieve important info */
3493 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
3494 f = fopen(fn2, "r");
3495 if (f) {
3496 while (!feof(f)) {
3497 fgets((char *)buf, sizeof(buf), f);
3498 if (!feof(f)) {
3499 char *stringp=NULL;
3500 stringp = (char *)buf;
3501 strsep(&stringp, "=");
3502 val = strsep(&stringp, "=");
3503 if (!ast_strlen_zero(val)) {
3504 if (!strcmp((char *)buf, "callerid"))
3505 ast_copy_string(cid, val, sizeof(cid));
3506 if (!strcmp((char *)buf, "origdate"))
3507 ast_copy_string(datetime, val, sizeof(datetime));
3511 fclose(f);
3513 /* New meaning for keys */
3514 for (x=0;x<5;x++)
3515 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3516 keys[6] = 0x0;
3517 keys[7] = 0x0;
3519 if (!vms->curmsg) {
3520 /* No prev key, provide "Folder" instead */
3521 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3523 if (vms->curmsg >= vms->lastmsg) {
3524 /* If last message ... */
3525 if (vms->curmsg) {
3526 /* but not only message, provide "Folder" instead */
3527 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3528 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3530 } else {
3531 /* Otherwise if only message, leave blank */
3532 keys[3] = 1;
3536 if (!ast_strlen_zero(cid)) {
3537 ast_callerid_parse(cid, &name, &num);
3538 if (!name)
3539 name = num;
3540 } else
3541 name = "Unknown Caller";
3543 /* If deleted, show "undeleted" */
3545 if (vms->deleted[vms->curmsg])
3546 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3548 /* Except "Exit" */
3549 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3550 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
3551 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
3552 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
3554 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3555 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3556 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
3557 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
3558 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3559 bytes += ast_adsi_set_keys(buf + bytes, keys);
3560 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3562 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3565 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
3567 int bytes=0;
3568 unsigned char buf[256];
3569 unsigned char keys[8];
3571 int x;
3573 if (!ast_adsi_available(chan))
3574 return;
3576 /* New meaning for keys */
3577 for (x=0;x<5;x++)
3578 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3580 keys[6] = 0x0;
3581 keys[7] = 0x0;
3583 if (!vms->curmsg) {
3584 /* No prev key, provide "Folder" instead */
3585 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3587 if (vms->curmsg >= vms->lastmsg) {
3588 /* If last message ... */
3589 if (vms->curmsg) {
3590 /* but not only message, provide "Folder" instead */
3591 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3592 } else {
3593 /* Otherwise if only message, leave blank */
3594 keys[3] = 1;
3598 /* If deleted, show "undeleted" */
3599 if (vms->deleted[vms->curmsg])
3600 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3602 /* Except "Exit" */
3603 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3604 bytes += ast_adsi_set_keys(buf + bytes, keys);
3605 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3607 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3610 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
3612 unsigned char buf[256] = "";
3613 char buf1[256] = "", buf2[256] = "";
3614 int bytes=0;
3615 unsigned char keys[8];
3616 int x;
3618 char *newm = (vms->newmessages == 1) ? "message" : "messages";
3619 char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
3620 if (!ast_adsi_available(chan))
3621 return;
3622 if (vms->newmessages) {
3623 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
3624 if (vms->oldmessages) {
3625 strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
3626 snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
3627 } else {
3628 snprintf(buf2, sizeof(buf2), "%s.", newm);
3630 } else if (vms->oldmessages) {
3631 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
3632 snprintf(buf2, sizeof(buf2), "%s.", oldm);
3633 } else {
3634 strcpy(buf1, "You have no messages.");
3635 buf2[0] = ' ';
3636 buf2[1] = '\0';
3638 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3639 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3640 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3642 for (x=0;x<6;x++)
3643 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3644 keys[6] = 0;
3645 keys[7] = 0;
3647 /* Don't let them listen if there are none */
3648 if (vms->lastmsg < 0)
3649 keys[0] = 1;
3650 bytes += ast_adsi_set_keys(buf + bytes, keys);
3652 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3654 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3657 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
3659 unsigned char buf[256] = "";
3660 char buf1[256] = "", buf2[256] = "";
3661 int bytes=0;
3662 unsigned char keys[8];
3663 int x;
3665 char *mess = (vms->lastmsg == 0) ? "message" : "messages";
3667 if (!ast_adsi_available(chan))
3668 return;
3670 /* Original command keys */
3671 for (x=0;x<6;x++)
3672 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3674 keys[6] = 0;
3675 keys[7] = 0;
3677 if ((vms->lastmsg + 1) < 1)
3678 keys[0] = 0;
3680 snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
3681 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
3683 if (vms->lastmsg + 1)
3684 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
3685 else
3686 strcpy(buf2, "no messages.");
3687 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3688 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3689 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
3690 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3691 bytes += ast_adsi_set_keys(buf + bytes, keys);
3693 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3695 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3700 static void adsi_clear(struct ast_channel *chan)
3702 char buf[256];
3703 int bytes=0;
3704 if (!ast_adsi_available(chan))
3705 return;
3706 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3707 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3709 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3713 static void adsi_goodbye(struct ast_channel *chan)
3715 unsigned char buf[256];
3716 int bytes=0;
3718 if (!ast_adsi_available(chan))
3719 return;
3720 bytes += adsi_logo(buf + bytes);
3721 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
3722 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
3723 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3724 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3726 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3729 /*--- get_folder: Folder menu ---*/
3730 /* Plays "press 1 for INBOX messages" etc
3731 Should possibly be internationalized
3733 static int get_folder(struct ast_channel *chan, int start)
3735 int x;
3736 int d;
3737 char fn[PATH_MAX];
3738 d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
3739 if (d)
3740 return d;
3741 for (x = start; x< 5; x++) { /* For all folders */
3742 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
3743 return d;
3744 d = ast_play_and_wait(chan, "vm-for"); /* "for" */
3745 if (d)
3746 return d;
3747 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
3748 d = vm_play_folder_name(chan, fn);
3749 if (d)
3750 return d;
3751 d = ast_waitfordigit(chan, 500);
3752 if (d)
3753 return d;
3755 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
3756 if (d)
3757 return d;
3758 d = ast_waitfordigit(chan, 4000);
3759 return d;
3762 static int get_folder2(struct ast_channel *chan, char *fn, int start)
3764 int res = 0;
3765 res = ast_play_and_wait(chan, fn); /* Folder name */
3766 while (((res < '0') || (res > '9')) &&
3767 (res != '#') && (res >= 0)) {
3768 res = get_folder(chan, 0);
3770 return res;
3773 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfmts,
3774 char *context, signed char record_gain, long *duration, struct vm_state *vms)
3776 int cmd = 0;
3777 int retries = 0;
3778 signed char zero_gain = 0;
3780 while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
3781 if (cmd)
3782 retries = 0;
3783 switch (cmd) {
3784 case '1':
3785 /* prepend a message to the current message, update the metadata and return */
3787 char msgfile[PATH_MAX];
3788 char textfile[PATH_MAX];
3789 int prepend_duration = 0;
3790 struct ast_config *msg_cfg;
3791 const char *duration_str;
3793 make_file(msgfile, sizeof(msgfile), curdir, curmsg);
3794 strcpy(textfile, msgfile);
3795 strncat(textfile, ".txt", sizeof(textfile) - 1);
3796 *duration = 0;
3798 /* if we can't read the message metadata, stop now */
3799 if (!(msg_cfg = ast_config_load(textfile))) {
3800 cmd = 0;
3801 break;
3804 if (record_gain)
3805 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
3807 cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vmfmts, &prepend_duration, 1, silencethreshold, maxsilence);
3808 if (record_gain)
3809 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
3812 if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
3813 *duration = atoi(duration_str);
3815 if (prepend_duration) {
3816 struct ast_category *msg_cat;
3817 /* need enough space for a maximum-length message duration */
3818 char duration_str[12];
3820 *duration += prepend_duration;
3821 msg_cat = ast_category_get(msg_cfg, "message");
3822 snprintf(duration_str, 11, "%ld", *duration);
3823 if (!ast_variable_update(msg_cat, "duration", duration_str, NULL, 0)) {
3824 config_text_file_save(textfile, msg_cfg, "app_voicemail");
3825 STORE(curdir, vmu->mailbox, context, curmsg, chan, vmu, vmfmts, *duration, vms);
3829 ast_config_destroy(msg_cfg);
3831 break;
3833 case '2':
3834 cmd = 't';
3835 break;
3836 case '*':
3837 cmd = '*';
3838 break;
3839 default:
3840 cmd = ast_play_and_wait(chan,"vm-forwardoptions");
3841 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
3842 if (!cmd)
3843 cmd = ast_play_and_wait(chan,"vm-starmain");
3844 /* "press star to return to the main menu" */
3845 if (!cmd)
3846 cmd = ast_waitfordigit(chan,6000);
3847 if (!cmd)
3848 retries++;
3849 if (retries > 3)
3850 cmd = 't';
3853 if (cmd == 't' || cmd == 'S')
3854 cmd = 0;
3855 return cmd;
3858 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
3860 char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
3861 int newmsgs = 0, oldmsgs = 0;
3862 const char *category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3864 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
3865 make_file(fn, sizeof(fn), todir, msgnum);
3866 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
3868 if (!ast_strlen_zero(vmu->attachfmt)) {
3869 if (strstr(fmt, vmu->attachfmt)) {
3870 fmt = vmu->attachfmt;
3871 } else {
3872 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);
3876 /* Attach only the first format */
3877 fmt = ast_strdupa(fmt);
3878 stringp = fmt;
3879 strsep(&stringp, "|");
3881 if (!ast_strlen_zero(vmu->email)) {
3882 int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
3883 char *myserveremail = serveremail;
3884 attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
3885 if (!ast_strlen_zero(vmu->serveremail))
3886 myserveremail = vmu->serveremail;
3888 if (attach_user_voicemail)
3889 RETRIEVE(todir, msgnum);
3891 /*XXX possible imap issue, should category be NULL XXX*/
3892 sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail, chan, category);
3894 if (attach_user_voicemail)
3895 DISPOSE(todir, msgnum);
3898 if (!ast_strlen_zero(vmu->pager)) {
3899 char *myserveremail = serveremail;
3900 if (!ast_strlen_zero(vmu->serveremail))
3901 myserveremail = vmu->serveremail;
3902 sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, duration, vmu, category);
3905 if (ast_test_flag(vmu, VM_DELETE)) {
3906 DELETE(todir, msgnum, fn);
3909 #ifdef IMAP_STORAGE
3910 DELETE(todir, msgnum, fn);
3911 #endif
3912 /* Leave voicemail for someone */
3913 if (ast_app_has_voicemail(ext_context, NULL)) {
3914 ast_app_inboxcount(ext_context, &newmsgs, &oldmsgs);
3916 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);
3917 run_externnotify(vmu->context, vmu->mailbox);
3918 return 0;
3921 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)
3923 #ifdef IMAP_STORAGE
3924 BODY *body;
3925 char *header_content;
3926 char *temp;
3927 char todir[256];
3928 int todircount=0;
3929 struct vm_state *dstvms;
3930 #endif
3931 char username[70]="";
3932 int res = 0, cmd = 0;
3933 struct ast_vm_user *receiver = NULL, *vmtmp;
3934 AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
3935 char *stringp;
3936 const char *s;
3937 int saved_messages = 0, found = 0;
3938 int valid_extensions = 0;
3939 char *dir;
3940 int curmsg;
3942 if (vms == NULL) return -1;
3943 dir = vms->curdir;
3944 curmsg = vms->curmsg;
3946 while (!res && !valid_extensions) {
3947 int use_directory = 0;
3948 if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
3949 int done = 0;
3950 int retries = 0;
3951 cmd=0;
3952 while ((cmd >= 0) && !done ){
3953 if (cmd)
3954 retries = 0;
3955 switch (cmd) {
3956 case '1':
3957 use_directory = 0;
3958 done = 1;
3959 break;
3960 case '2':
3961 use_directory = 1;
3962 done=1;
3963 break;
3964 case '*':
3965 cmd = 't';
3966 done = 1;
3967 break;
3968 default:
3969 /* Press 1 to enter an extension press 2 to use the directory */
3970 cmd = ast_play_and_wait(chan,"vm-forward");
3971 if (!cmd)
3972 cmd = ast_waitfordigit(chan,3000);
3973 if (!cmd)
3974 retries++;
3975 if (retries > 3)
3977 cmd = 't';
3978 done = 1;
3983 if (cmd < 0 || cmd == 't')
3984 break;
3987 if (use_directory) {
3988 /* use app_directory */
3990 char old_context[sizeof(chan->context)];
3991 char old_exten[sizeof(chan->exten)];
3992 int old_priority;
3993 struct ast_app* app;
3996 app = pbx_findapp("Directory");
3997 if (app) {
3998 char vmcontext[256];
3999 /* make backup copies */
4000 memcpy(old_context, chan->context, sizeof(chan->context));
4001 memcpy(old_exten, chan->exten, sizeof(chan->exten));
4002 old_priority = chan->priority;
4004 /* call the the Directory, changes the channel */
4005 snprintf(vmcontext, sizeof(vmcontext), "%s||v", context ? context : "default");
4006 res = pbx_exec(chan, app, vmcontext);
4008 ast_copy_string(username, chan->exten, sizeof(username));
4010 /* restore the old context, exten, and priority */
4011 memcpy(chan->context, old_context, sizeof(chan->context));
4012 memcpy(chan->exten, old_exten, sizeof(chan->exten));
4013 chan->priority = old_priority;
4015 } else {
4016 ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
4017 ast_clear_flag((&globalflags), VM_DIRECFORWARD);
4019 } else {
4020 /* Ask for an extension */
4021 res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
4022 if (res)
4023 break;
4024 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
4025 break;
4028 /* start all over if no username */
4029 if (ast_strlen_zero(username))
4030 continue;
4031 stringp = username;
4032 s = strsep(&stringp, "*");
4033 /* start optimistic */
4034 valid_extensions = 1;
4035 while (s) {
4036 /* Don't forward to ourselves. find_user is going to malloc since we have a NULL as first argument */
4037 if (strcmp(s,sender->mailbox) && (receiver = find_user(NULL, context, s))) {
4038 AST_LIST_INSERT_HEAD(&extensions, receiver, list);
4039 found++;
4040 } else {
4041 valid_extensions = 0;
4042 break;
4044 s = strsep(&stringp, "*");
4046 /* break from the loop of reading the extensions */
4047 if (valid_extensions)
4048 break;
4049 /* "I am sorry, that's not a valid extension. Please try again." */
4050 res = ast_play_and_wait(chan, "pbx-invalid");
4052 /* check if we're clear to proceed */
4053 if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
4054 return res;
4055 if (flag==1) {
4056 struct leave_vm_options leave_options;
4057 char mailbox[AST_MAX_EXTENSION * 2 + 2];
4058 snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
4060 /* Send VoiceMail */
4061 memset(&leave_options, 0, sizeof(leave_options));
4062 leave_options.record_gain = record_gain;
4063 cmd = leave_voicemail(chan, mailbox, &leave_options);
4064 } else {
4066 /* Forward VoiceMail */
4067 long duration = 0;
4068 RETRIEVE(dir, curmsg);
4069 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, vms);
4070 if (!cmd) {
4071 AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
4072 #ifdef IMAP_STORAGE
4073 char *myserveremail;
4074 int attach_user_voicemail;
4075 /* Need to get message content */
4076 if(option_debug > 2)
4077 ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
4078 if (vms->msgArray[vms->curmsg] == 0) {
4079 ast_log (LOG_WARNING,"Trying to access unknown message\n");
4080 return -1;
4083 /* This will only work for new messages... */
4084 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
4085 /* empty string means no valid header */
4086 if (ast_strlen_zero(header_content)) {
4087 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
4088 return -1;
4090 /* Get header info needed by sendmail */
4091 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:");
4092 if (temp)
4093 duration = atoi(temp);
4094 else
4095 duration = 0;
4097 /* Attach only the first format */
4098 fmt = ast_strdupa(fmt);
4099 if (fmt) {
4100 stringp = fmt;
4101 strsep(&stringp, "|");
4102 } else {
4103 ast_log (LOG_ERROR,"audio format not set. Default to WAV\n");
4104 fmt = "WAV";
4106 if (!strcasecmp(fmt, "wav49"))
4107 fmt = "WAV";
4108 if(option_debug > 2)
4109 ast_log (LOG_DEBUG,"**** format set to %s, vmfmts set to %s\n",fmt,vmfmts);
4110 /* ast_copy_string(fmt, vmfmts, sizeof(fmt));*/
4111 /* if (!ast_strlen_zero(fmt)) { */
4112 snprintf(todir, sizeof(todir), "%s%s/%s/tmp", VM_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
4113 make_gsm_file(vms->fn, sizeof(vms->fn), vms->imapuser, todir, vms->curmsg);
4114 if(option_debug > 2)
4115 ast_log (LOG_DEBUG,"Before mail_fetchstructure, message number is %ld, filename is:%s\n",vms->msgArray[vms->curmsg], vms->fn);
4116 /*mail_fetchstructure (mailstream, vmArray[0], &body); */
4117 mail_fetchstructure (vms->mailstream, vms->msgArray[vms->curmsg], &body);
4118 save_body(body,vms,"3","gsm");
4119 /* should not assume "fmt" here! */
4120 save_body(body,vms,"2",fmt);
4122 /* get destination mailbox */
4123 dstvms = get_vm_state_by_mailbox(vmtmp->mailbox,0);
4124 if (dstvms) {
4125 init_mailstream(dstvms, 0);
4126 if (!dstvms->mailstream) {
4127 ast_log (LOG_ERROR,"IMAP mailstream for %s is NULL\n",vmtmp->mailbox);
4128 } else {
4129 STORE(todir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms);
4130 run_externnotify(vmtmp->context, vmtmp->mailbox);
4132 } else {
4133 ast_log (LOG_ERROR,"Could not find state information for mailbox %s\n",vmtmp->mailbox);
4136 myserveremail = serveremail;
4137 if (!ast_strlen_zero(vmtmp->serveremail))
4138 myserveremail = vmtmp->serveremail;
4139 attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
4140 attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
4141 /* NULL category for IMAP storage */
4142 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);
4143 #else
4144 copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir);
4145 #endif
4146 saved_messages++;
4147 AST_LIST_REMOVE_CURRENT(&extensions, list);
4148 free_user(vmtmp);
4149 if (res)
4150 break;
4152 AST_LIST_TRAVERSE_SAFE_END;
4153 if (saved_messages > 0) {
4154 /* give confirmation that the message was saved */
4155 /* commented out since we can't forward batches yet
4156 if (saved_messages == 1)
4157 res = ast_play_and_wait(chan, "vm-message");
4158 else
4159 res = ast_play_and_wait(chan, "vm-messages");
4160 if (!res)
4161 res = ast_play_and_wait(chan, "vm-saved"); */
4162 res = ast_play_and_wait(chan, "vm-msgsaved");
4167 /* If anything failed above, we still have this list to free */
4168 while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list)))
4169 free_user(vmtmp);
4170 return res ? res : cmd;
4173 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
4175 int res;
4176 if ((res = ast_stream_and_wait(chan, file, chan->language, AST_DIGIT_ANY)) < 0)
4177 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
4178 return res;
4181 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
4183 return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", "2", skipms);
4186 static int play_message_category(struct ast_channel *chan, const char *category)
4188 int res = 0;
4190 if (!ast_strlen_zero(category))
4191 res = ast_play_and_wait(chan, category);
4193 if (res) {
4194 ast_log(LOG_WARNING, "No sound file for category '%s' was found.\n", category);
4195 res = 0;
4198 return res;
4201 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
4203 int res = 0;
4204 struct vm_zone *the_zone = NULL;
4205 time_t t;
4207 if (ast_get_time_t(origtime, &t, 0, NULL)) {
4208 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
4209 return 0;
4212 /* Does this user have a timezone specified? */
4213 if (!ast_strlen_zero(vmu->zonetag)) {
4214 /* Find the zone in the list */
4215 struct vm_zone *z;
4216 AST_LIST_LOCK(&zones);
4217 AST_LIST_TRAVERSE(&zones, z, list) {
4218 if (!strcmp(z->name, vmu->zonetag)) {
4219 the_zone = z;
4220 break;
4223 AST_LIST_UNLOCK(&zones);
4226 /* No internal variable parsing for now, so we'll comment it out for the time being */
4227 #if 0
4228 /* Set the DIFF_* variables */
4229 ast_localtime(&t, &time_now, NULL);
4230 tv_now = ast_tvnow();
4231 tnow = tv_now.tv_sec;
4232 ast_localtime(&tnow, &time_then, NULL);
4234 /* Day difference */
4235 if (time_now.tm_year == time_then.tm_year)
4236 snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
4237 else
4238 snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
4239 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
4241 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
4242 #endif
4243 if (the_zone)
4244 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
4245 else if (!strcasecmp(chan->language,"pl")) /* POLISH syntax */
4246 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
4247 else if (!strcasecmp(chan->language,"se")) /* SWEDISH syntax */
4248 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
4249 else if (!strcasecmp(chan->language,"no")) /* NORWEGIAN syntax */
4250 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
4251 else if (!strcasecmp(chan->language,"de")) /* GERMAN syntax */
4252 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
4253 else if (!strcasecmp(chan->language,"nl")) /* DUTCH syntax */
4254 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
4255 else if (!strcasecmp(chan->language,"it")) /* ITALIAN syntax */
4256 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);
4257 else if (!strcasecmp(chan->language,"gr"))
4258 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q H 'digits/kai' M ", NULL);
4259 else if (!strcasecmp(chan->language,"pt_BR"))
4260 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);
4261 else
4262 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
4263 #if 0
4264 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
4265 #endif
4266 return res;
4271 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
4273 int res = 0;
4274 int i;
4275 char *callerid, *name;
4276 char prefile[PATH_MAX] = "";
4279 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
4280 /* 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 */
4281 if ((cid == NULL)||(context == NULL))
4282 return res;
4284 /* Strip off caller ID number from name */
4285 if (option_debug > 2)
4286 ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
4287 ast_callerid_parse(cid, &name, &callerid);
4288 if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
4289 /* Check for internal contexts and only */
4290 /* say extension when the call didn't come from an internal context in the list */
4291 for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
4292 if (option_debug > 2)
4293 ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
4294 if ((strcmp(cidinternalcontexts[i], context) == 0))
4295 break;
4297 if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
4298 if (!res) {
4299 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
4300 if (!ast_strlen_zero(prefile)) {
4301 /* See if we can find a recorded name for this person instead of their extension number */
4302 if (ast_fileexists(prefile, NULL, NULL) > 0) {
4303 if (option_verbose > 2)
4304 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
4305 if (!callback)
4306 res = wait_file2(chan, vms, "vm-from");
4307 res = ast_stream_and_wait(chan, prefile, chan->language, "");
4308 } else {
4309 if (option_verbose > 2)
4310 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
4311 /* BB: Say "from extension" as one saying to sound smoother */
4312 if (!callback)
4313 res = wait_file2(chan, vms, "vm-from-extension");
4314 res = ast_say_digit_str(chan, callerid, "", chan->language);
4320 else if (!res){
4321 if (option_debug > 2)
4322 ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
4323 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
4324 if (!callback)
4325 res = wait_file2(chan, vms, "vm-from-phonenumber");
4326 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
4328 } else {
4329 /* Number unknown */
4330 if (option_debug)
4331 ast_log(LOG_DEBUG, "VM-CID: From an unknown number\n");
4332 /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
4333 res = wait_file2(chan, vms, "vm-unknown-caller");
4335 return res;
4338 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
4340 int res = 0;
4341 int durationm;
4342 int durations;
4343 /* Verify that we have a duration for the message */
4344 if (duration == NULL)
4345 return res;
4347 /* Convert from seconds to minutes */
4348 durations=atoi(duration);
4349 durationm=(durations / 60);
4351 if (option_debug > 2)
4352 ast_log(LOG_DEBUG, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
4354 if ((!res) && (durationm >= minduration)) {
4355 res = wait_file2(chan, vms, "vm-duration");
4357 /* POLISH syntax */
4358 if (!strcasecmp(chan->language, "pl")) {
4359 div_t num = div(durationm, 10);
4361 if (durationm == 1) {
4362 res = ast_play_and_wait(chan, "digits/1z");
4363 res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
4364 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
4365 if (num.rem == 2) {
4366 if (!num.quot) {
4367 res = ast_play_and_wait(chan, "digits/2-ie");
4368 } else {
4369 res = say_and_wait(chan, durationm - 2 , chan->language);
4370 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
4372 } else {
4373 res = say_and_wait(chan, durationm, chan->language);
4375 res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
4376 } else {
4377 res = say_and_wait(chan, durationm, chan->language);
4378 res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
4380 /* DEFAULT syntax */
4381 } else {
4382 res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
4383 res = wait_file2(chan, vms, "vm-minutes");
4386 return res;
4389 #ifdef IMAP_STORAGE
4390 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
4392 BODY *body;
4393 char *header_content;
4394 char cid[256];
4395 char context[256];
4396 char origtime[32];
4397 char duration[16];
4398 char category[32];
4399 char todir[PATH_MAX];
4400 int res = 0;
4401 char *attachedfilefmt;
4402 char *temp;
4404 vms->starting = 0;
4405 if(option_debug > 2)
4406 ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
4407 if (vms->msgArray[vms->curmsg] == 0) {
4408 ast_log (LOG_WARNING,"Trying to access unknown message\n");
4409 return -1;
4412 /* This will only work for new messages... */
4413 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
4414 /* empty string means no valid header */
4415 if (ast_strlen_zero(header_content)) {
4416 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
4417 return -1;
4419 snprintf(todir, sizeof(todir), "%s%s/%s/tmp", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
4420 make_gsm_file(vms->fn, sizeof(vms->fn), vms->imapuser, todir, vms->curmsg);
4422 mail_fetchstructure (vms->mailstream,vms->msgArray[vms->curmsg],&body);
4424 /* We have the body, now we extract the file name of the first attachment. */
4425 if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
4426 attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
4427 } else {
4428 ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
4429 return -1;
4432 /* Find the format of the attached file */
4434 strsep(&attachedfilefmt, ".");
4435 if (!attachedfilefmt) {
4436 ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
4437 return -1;
4439 save_body(body, vms, "2", attachedfilefmt);
4441 adsi_message(chan, vms);
4442 if (!vms->curmsg)
4443 res = wait_file2(chan, vms, "vm-first"); /* "First" */
4444 else if (vms->curmsg == vms->lastmsg)
4445 res = wait_file2(chan, vms, "vm-last"); /* "last" */
4446 if (!res) {
4447 res = wait_file2(chan, vms, "vm-message"); /* "message" */
4448 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
4449 if (!res)
4450 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
4454 /* Get info from headers!! */
4455 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:");
4457 if (temp)
4458 ast_copy_string(cid, temp, sizeof(cid));
4459 else
4460 cid[0] = '\0';
4462 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Context:");
4464 if (temp)
4465 ast_copy_string(context, temp, sizeof(context));
4466 else
4467 context[0] = '\0';
4469 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:");
4471 if (temp)
4472 ast_copy_string(origtime, temp, sizeof(origtime));
4473 else
4474 origtime[0] = '\0';
4476 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:");
4478 if (temp)
4479 ast_copy_string(duration,temp, sizeof(duration));
4480 else
4481 duration[0] = '\0';
4483 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Category:");
4485 if (temp)
4486 ast_copy_string(category,temp, sizeof(category));
4487 else
4488 category[0] = '\0';
4490 /*if (!strncasecmp("macro",context,5)) Macro names in contexts are useless for our needs */
4491 /* context = ast_variable_retrieve(msg_cfg, "message","macrocontext"); */
4492 if (res == '1')
4493 res = 0;
4495 if ((!res) && !ast_strlen_zero(category)) {
4496 res = play_message_category(chan, category);
4499 if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)) && origtime[0] != '\0')
4500 res = play_message_datetime(chan, vmu, origtime, "IMAP_STORAGE");
4501 if ((!res) && (ast_test_flag(vmu, VM_SAYCID)) && cid[0] !='\0' && context[0] !='\0')
4502 res = play_message_callerid(chan, vms, cid, context, 0);
4504 if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)) && duration[0] != '\0')
4505 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
4507 /* Allow pressing '1' to skip envelope / callerid */
4508 /* if (res == '1')
4509 res = 0;
4511 /*ast_config_destroy(msg_cfg);*/
4512 res = 0;
4514 if (!res) {
4515 vms->heard[vms->curmsg] = 1;
4516 res = wait_file(chan, vms, vms->fn);
4518 DISPOSE(vms->curdir, vms->curmsg);
4519 DELETE(0, 0, vms->fn);
4520 return res;
4522 #else
4523 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
4525 int res = 0;
4526 char filename[256], *cid;
4527 const char *origtime, *context, *category, *duration;
4528 struct ast_config *msg_cfg;
4530 vms->starting = 0;
4531 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
4532 adsi_message(chan, vms);
4533 if (!vms->curmsg)
4534 res = wait_file2(chan, vms, "vm-first"); /* "First" */
4535 else if (vms->curmsg == vms->lastmsg)
4536 res = wait_file2(chan, vms, "vm-last"); /* "last" */
4537 if (!res) {
4538 /* POLISH syntax */
4539 if (!strcasecmp(chan->language, "pl")) {
4540 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
4541 int ten, one;
4542 char nextmsg[256];
4543 ten = (vms->curmsg + 1) / 10;
4544 one = (vms->curmsg + 1) % 10;
4546 if (vms->curmsg < 20) {
4547 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
4548 res = wait_file2(chan, vms, nextmsg);
4549 } else {
4550 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
4551 res = wait_file2(chan, vms, nextmsg);
4552 if (one > 0) {
4553 if (!res) {
4554 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
4555 res = wait_file2(chan, vms, nextmsg);
4560 if (!res)
4561 res = wait_file2(chan, vms, "vm-message");
4562 } else {
4563 if (!strcasecmp(chan->language, "se")) /* SWEDISH syntax */
4564 res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
4565 else /* DEFAULT syntax */
4566 res = wait_file2(chan, vms, "vm-message");
4567 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
4568 if (!res)
4569 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
4574 /* Retrieve info from VM attribute file */
4575 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
4576 snprintf(filename, sizeof(filename), "%s.txt", vms->fn2);
4577 RETRIEVE(vms->curdir, vms->curmsg);
4578 msg_cfg = ast_config_load(filename);
4579 if (!msg_cfg) {
4580 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
4581 return 0;
4584 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
4585 ast_log(LOG_WARNING, "No origtime?!\n");
4586 DISPOSE(vms->curdir, vms->curmsg);
4587 ast_config_destroy(msg_cfg);
4588 return 0;
4591 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
4592 duration = ast_variable_retrieve(msg_cfg, "message", "duration");
4593 category = ast_variable_retrieve(msg_cfg, "message", "category");
4595 context = ast_variable_retrieve(msg_cfg, "message", "context");
4596 if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
4597 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
4598 if (!res)
4599 res = play_message_category(chan, category);
4600 if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
4601 res = play_message_datetime(chan, vmu, origtime, filename);
4602 if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
4603 res = play_message_callerid(chan, vms, cid, context, 0);
4604 if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
4605 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
4606 /* Allow pressing '1' to skip envelope / callerid */
4607 if (res == '1')
4608 res = 0;
4609 ast_config_destroy(msg_cfg);
4611 if (!res) {
4612 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
4613 vms->heard[vms->curmsg] = 1;
4614 res = wait_file(chan, vms, vms->fn);
4616 DISPOSE(vms->curdir, vms->curmsg);
4617 return res;
4619 #endif
4621 #ifdef IMAP_STORAGE
4622 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
4624 char tmp[256], *t = tmp;
4625 size_t left = sizeof(tmp);
4627 if (box == 1) {
4628 ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
4629 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(1));
4630 } else {
4631 ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
4632 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
4635 /* Build up server information */
4636 ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
4638 /* Add authentication user if present */
4639 if (!ast_strlen_zero(authuser))
4640 ast_build_string(&t, &left, "/authuser=%s", authuser);
4642 /* Add flags if present */
4643 if (!ast_strlen_zero(imapflags))
4644 ast_build_string(&t, &left, "/%s", imapflags);
4646 /* End with username */
4647 ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
4649 if(box == 0 || box == 1)
4650 snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
4651 else
4652 snprintf(spec, len, "%s%s%c%s", tmp, imapfolder, delimiter, mbox(box));
4655 static int init_mailstream(struct vm_state *vms, int box)
4657 MAILSTREAM *stream = NIL;
4658 long debug;
4659 char tmp[256];
4661 if (!vms) {
4662 ast_log (LOG_ERROR,"vm_state is NULL!\n");
4663 return -1;
4665 if(option_debug > 2)
4666 ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
4667 if (vms->mailstream == NIL || !vms->mailstream) {
4668 if (option_debug)
4669 ast_log (LOG_DEBUG,"mailstream not set.\n");
4670 } else {
4671 stream = vms->mailstream;
4673 /* debug = T; user wants protocol telemetry? */
4674 debug = NIL; /* NO protocol telemetry? */
4676 if (delimiter == '\0') { /* did not probe the server yet */
4677 char *cp;
4678 #include "linkage.c"
4679 /* Connect to INBOX first to get folders delimiter */
4680 imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 0);
4681 stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
4682 if (stream == NIL) {
4683 ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
4684 return NIL;
4686 get_mailbox_delimiter(stream);
4687 /* update delimiter in imapfolder */
4688 for(cp = imapfolder; *cp; cp++)
4689 if(*cp == '/')
4690 *cp = delimiter;
4692 /* Now connect to the target folder */
4693 imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
4694 if(option_debug > 2)
4695 ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
4696 vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
4697 if (vms->mailstream == NIL) {
4698 return -1;
4699 } else {
4700 return 0;
4704 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
4706 SEARCHPGM *pgm;
4707 SEARCHHEADER *hdr;
4708 int ret;
4709 char dbox[256];
4711 ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
4712 if(option_debug > 2)
4713 ast_log(LOG_DEBUG,"Before init_mailstream, user is %s\n",vmu->imapuser);
4714 ret = init_mailstream(vms, box);
4715 if (ret != 0 || !vms->mailstream) {
4716 ast_log (LOG_ERROR,"Could not initialize mailstream\n");
4717 return -1;
4720 pgm = mail_newsearchpgm();
4722 /* Check IMAP folder for Asterisk messages only... */
4723 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", vmu->mailbox);
4724 pgm->header = hdr;
4725 pgm->deleted = 0;
4726 pgm->undeleted = 1;
4728 /* if box = 0, check for new, if box = 1, check for read */
4729 if (box == 0) {
4730 pgm->unseen = 1;
4731 pgm->seen = 0;
4732 } else if (box == 1) {
4733 pgm->seen = 1;
4734 pgm->unseen = 0;
4737 vms->vmArrayIndex = 0;
4738 if(option_debug > 2)
4739 ast_log(LOG_DEBUG,"Before mail_search_full, user is %s\n",vmu->imapuser);
4740 mail_search_full (vms->mailstream, NULL, pgm, NIL);
4743 vms->lastmsg = vms->vmArrayIndex - 1;
4745 mail_free_searchpgm(&pgm);
4746 return 0;
4748 #else
4749 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
4751 int res = 0;
4752 int count_msg, last_msg;
4754 ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
4756 /* Rename the member vmbox HERE so that we don't try to return before
4757 * we know what's going on.
4759 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
4761 /* Faster to make the directory than to check if it exists. */
4762 create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
4764 count_msg = count_messages(vmu, vms->curdir);
4765 if (count_msg < 0)
4766 return count_msg;
4767 else
4768 vms->lastmsg = count_msg - 1;
4771 The following test is needed in case sequencing gets messed up.
4772 There appears to be more than one way to mess up sequence, so
4773 we will not try to find all of the root causes--just fix it when
4774 detected.
4777 last_msg = last_message_index(vmu, vms->curdir);
4778 if (last_msg < 0)
4779 return last_msg;
4780 else if (vms->lastmsg != last_msg)
4782 ast_log(LOG_NOTICE, "Resequencing Mailbox: %s\n", vms->curdir);
4783 res = resequence_mailbox(vmu, vms->curdir);
4784 if (res)
4785 return res;
4788 return 0;
4790 #endif
4792 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
4794 int x = 0;
4795 #ifndef IMAP_STORAGE
4796 int res = 0, nummsg;
4797 #endif
4799 if (vms->lastmsg <= -1)
4800 goto done;
4802 vms->curmsg = -1;
4803 #ifndef IMAP_STORAGE
4804 /* Get the deleted messages fixed */
4805 if (vm_lock_path(vms->curdir))
4806 return ERROR_LOCK_PATH;
4808 for (x = 0; x < vmu->maxmsg; x++) {
4809 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
4810 /* Save this message. It's not in INBOX or hasn't been heard */
4811 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
4812 if (!EXISTS(vms->curdir, x, vms->fn, NULL))
4813 break;
4814 vms->curmsg++;
4815 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
4816 if (strcmp(vms->fn, vms->fn2)) {
4817 RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
4819 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
4820 /* Move to old folder before deleting */
4821 res = save_to_folder(vmu, vms, x, 1);
4822 if (res == ERROR_LOCK_PATH) {
4823 /* If save failed do not delete the message */
4824 vms->deleted[x] = 0;
4825 vms->heard[x] = 0;
4826 --x;
4831 /* Delete ALL remaining messages */
4832 nummsg = x - 1;
4833 for (x = vms->curmsg + 1; x <= nummsg; x++) {
4834 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
4835 if (EXISTS(vms->curdir, x, vms->fn, NULL))
4836 DELETE(vms->curdir, x, vms->fn);
4838 ast_unlock_path(vms->curdir);
4839 #else
4840 if (vms->deleted) {
4841 for (x=0;x < vmu->maxmsg;x++) {
4842 if (vms->deleted[x]) {
4843 if(option_debug > 2)
4844 ast_log(LOG_DEBUG,"IMAP delete of %d\n",x);
4845 IMAP_DELETE(vms->curdir, x, vms->fn, vms);
4849 #endif
4851 done:
4852 if (vms->deleted)
4853 memset(vms->deleted, 0, vmu->maxmsg * sizeof(int));
4854 if (vms->heard)
4855 memset(vms->heard, 0, vmu->maxmsg * sizeof(int));
4857 return 0;
4860 /* In Greek even though we CAN use a syntax like "friends messages"
4861 * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
4862 * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
4863 * syntax for the above three categories which is more elegant.
4866 static int vm_play_folder_name_gr(struct ast_channel *chan, char *mbox)
4868 int cmd;
4869 char *buf;
4871 buf = alloca(strlen(mbox)+2);
4872 strcpy(buf, mbox);
4873 strcat(buf,"s");
4875 if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")){
4876 cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
4877 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
4878 } else {
4879 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
4880 return cmd ? cmd : ast_play_and_wait(chan, mbox); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
4884 static int vm_play_folder_name_pl(struct ast_channel *chan, char *mbox)
4886 int cmd;
4888 if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")) {
4889 if (!strcasecmp(mbox, "vm-INBOX"))
4890 cmd = ast_play_and_wait(chan, "vm-new-e");
4891 else
4892 cmd = ast_play_and_wait(chan, "vm-old-e");
4893 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
4894 } else {
4895 cmd = ast_play_and_wait(chan, "vm-messages");
4896 return cmd ? cmd : ast_play_and_wait(chan, mbox);
4900 static int vm_play_folder_name_ua(struct ast_channel *chan, char *mbox)
4902 int cmd;
4904 if (!strcasecmp(mbox, "vm-Family") || !strcasecmp(mbox, "vm-Friends") || !strcasecmp(mbox, "vm-Work")){
4905 cmd = ast_play_and_wait(chan, "vm-messages");
4906 return cmd ? cmd : ast_play_and_wait(chan, mbox);
4907 } else {
4908 cmd = ast_play_and_wait(chan, mbox);
4909 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
4913 static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
4915 int cmd;
4917 if (!strcasecmp(chan->language, "it") || !strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* Italian, Spanish, French or Portuguese syntax */
4918 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
4919 return cmd ? cmd : ast_play_and_wait(chan, mbox);
4920 } else if (!strcasecmp(chan->language, "gr")){
4921 return vm_play_folder_name_gr(chan, mbox);
4922 } else if (!strcasecmp(chan->language, "pl")){
4923 return vm_play_folder_name_pl(chan, mbox);
4924 } else if (!strcasecmp(chan->language, "ua")){ /* Ukrainian syntax */
4925 return vm_play_folder_name_ua(chan, mbox);
4926 } else { /* Default English */
4927 cmd = ast_play_and_wait(chan, mbox);
4928 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
4932 /* GREEK SYNTAX
4933 In greek the plural for old/new is
4934 different so we need the following files
4935 We also need vm-denExeteMynhmata because
4936 this syntax is different.
4938 -> vm-Olds.wav : "Palia"
4939 -> vm-INBOXs.wav : "Nea"
4940 -> vm-denExeteMynhmata : "den exete mynhmata"
4944 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
4946 int res = 0;
4948 if (vms->newmessages) {
4949 res = ast_play_and_wait(chan, "vm-youhave");
4950 if (!res)
4951 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
4952 if (!res) {
4953 if ((vms->newmessages == 1)) {
4954 res = ast_play_and_wait(chan, "vm-INBOX");
4955 if (!res)
4956 res = ast_play_and_wait(chan, "vm-message");
4957 } else {
4958 res = ast_play_and_wait(chan, "vm-INBOXs");
4959 if (!res)
4960 res = ast_play_and_wait(chan, "vm-messages");
4963 } else if (vms->oldmessages){
4964 res = ast_play_and_wait(chan, "vm-youhave");
4965 if (!res)
4966 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
4967 if ((vms->oldmessages == 1)){
4968 res = ast_play_and_wait(chan, "vm-Old");
4969 if (!res)
4970 res = ast_play_and_wait(chan, "vm-message");
4971 } else {
4972 res = ast_play_and_wait(chan, "vm-Olds");
4973 if (!res)
4974 res = ast_play_and_wait(chan, "vm-messages");
4976 } else if (!vms->oldmessages && !vms->newmessages)
4977 res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
4978 return res;
4981 /* Default English syntax */
4982 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
4984 int res;
4986 /* Introduce messages they have */
4987 res = ast_play_and_wait(chan, "vm-youhave");
4988 if (!res) {
4989 if (vms->newmessages) {
4990 res = say_and_wait(chan, vms->newmessages, chan->language);
4991 if (!res)
4992 res = ast_play_and_wait(chan, "vm-INBOX");
4993 if (vms->oldmessages && !res)
4994 res = ast_play_and_wait(chan, "vm-and");
4995 else if (!res) {
4996 if ((vms->newmessages == 1))
4997 res = ast_play_and_wait(chan, "vm-message");
4998 else
4999 res = ast_play_and_wait(chan, "vm-messages");
5003 if (!res && vms->oldmessages) {
5004 res = say_and_wait(chan, vms->oldmessages, chan->language);
5005 if (!res)
5006 res = ast_play_and_wait(chan, "vm-Old");
5007 if (!res) {
5008 if (vms->oldmessages == 1)
5009 res = ast_play_and_wait(chan, "vm-message");
5010 else
5011 res = ast_play_and_wait(chan, "vm-messages");
5014 if (!res) {
5015 if (!vms->oldmessages && !vms->newmessages) {
5016 res = ast_play_and_wait(chan, "vm-no");
5017 if (!res)
5018 res = ast_play_and_wait(chan, "vm-messages");
5022 return res;
5025 /* ITALIAN syntax */
5026 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
5028 /* Introduce messages they have */
5029 int res;
5030 if (!vms->oldmessages && !vms->newmessages)
5031 res = ast_play_and_wait(chan, "vm-no") ||
5032 ast_play_and_wait(chan, "vm-message");
5033 else
5034 res = ast_play_and_wait(chan, "vm-youhave");
5035 if (!res && vms->newmessages) {
5036 res = (vms->newmessages == 1) ?
5037 ast_play_and_wait(chan, "digits/un") ||
5038 ast_play_and_wait(chan, "vm-nuovo") ||
5039 ast_play_and_wait(chan, "vm-message") :
5040 /* 2 or more new messages */
5041 say_and_wait(chan, vms->newmessages, chan->language) ||
5042 ast_play_and_wait(chan, "vm-nuovi") ||
5043 ast_play_and_wait(chan, "vm-messages");
5044 if (!res && vms->oldmessages)
5045 res = ast_play_and_wait(chan, "vm-and");
5047 if (!res && vms->oldmessages) {
5048 res = (vms->oldmessages == 1) ?
5049 ast_play_and_wait(chan, "digits/un") ||
5050 ast_play_and_wait(chan, "vm-vecchio") ||
5051 ast_play_and_wait(chan, "vm-message") :
5052 /* 2 or more old messages */
5053 say_and_wait(chan, vms->oldmessages, chan->language) ||
5054 ast_play_and_wait(chan, "vm-vecchi") ||
5055 ast_play_and_wait(chan, "vm-messages");
5057 return res ? -1 : 0;
5060 /* POLISH syntax */
5061 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
5063 /* Introduce messages they have */
5064 int res;
5065 div_t num;
5067 if (!vms->oldmessages && !vms->newmessages) {
5068 res = ast_play_and_wait(chan, "vm-no");
5069 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5070 return res;
5071 } else {
5072 res = ast_play_and_wait(chan, "vm-youhave");
5075 if (vms->newmessages) {
5076 num = div(vms->newmessages, 10);
5077 if (vms->newmessages == 1) {
5078 res = ast_play_and_wait(chan, "digits/1-a");
5079 res = res ? res : ast_play_and_wait(chan, "vm-new-a");
5080 res = res ? res : ast_play_and_wait(chan, "vm-message");
5081 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
5082 if (num.rem == 2) {
5083 if (!num.quot) {
5084 res = ast_play_and_wait(chan, "digits/2-ie");
5085 } else {
5086 res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
5087 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
5089 } else {
5090 res = say_and_wait(chan, vms->newmessages, chan->language);
5092 res = res ? res : ast_play_and_wait(chan, "vm-new-e");
5093 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5094 } else {
5095 res = say_and_wait(chan, vms->newmessages, chan->language);
5096 res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
5097 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5099 if (!res && vms->oldmessages)
5100 res = ast_play_and_wait(chan, "vm-and");
5102 if (!res && vms->oldmessages) {
5103 num = div(vms->oldmessages, 10);
5104 if (vms->oldmessages == 1) {
5105 res = ast_play_and_wait(chan, "digits/1-a");
5106 res = res ? res : ast_play_and_wait(chan, "vm-old-a");
5107 res = res ? res : ast_play_and_wait(chan, "vm-message");
5108 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
5109 if (num.rem == 2) {
5110 if (!num.quot) {
5111 res = ast_play_and_wait(chan, "digits/2-ie");
5112 } else {
5113 res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
5114 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
5116 } else {
5117 res = say_and_wait(chan, vms->oldmessages, chan->language);
5119 res = res ? res : ast_play_and_wait(chan, "vm-old-e");
5120 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5121 } else {
5122 res = say_and_wait(chan, vms->oldmessages, chan->language);
5123 res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
5124 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5128 return res;
5131 /* SWEDISH syntax */
5132 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
5134 /* Introduce messages they have */
5135 int res;
5137 res = ast_play_and_wait(chan, "vm-youhave");
5138 if (res)
5139 return res;
5141 if (!vms->oldmessages && !vms->newmessages) {
5142 res = ast_play_and_wait(chan, "vm-no");
5143 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5144 return res;
5147 if (vms->newmessages) {
5148 if ((vms->newmessages == 1)) {
5149 res = ast_play_and_wait(chan, "digits/ett");
5150 res = res ? res : ast_play_and_wait(chan, "vm-nytt");
5151 res = res ? res : ast_play_and_wait(chan, "vm-message");
5152 } else {
5153 res = say_and_wait(chan, vms->newmessages, chan->language);
5154 res = res ? res : ast_play_and_wait(chan, "vm-nya");
5155 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5157 if (!res && vms->oldmessages)
5158 res = ast_play_and_wait(chan, "vm-and");
5160 if (!res && vms->oldmessages) {
5161 if (vms->oldmessages == 1) {
5162 res = ast_play_and_wait(chan, "digits/ett");
5163 res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
5164 res = res ? res : ast_play_and_wait(chan, "vm-message");
5165 } else {
5166 res = say_and_wait(chan, vms->oldmessages, chan->language);
5167 res = res ? res : ast_play_and_wait(chan, "vm-gamla");
5168 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5172 return res;
5175 /* NORWEGIAN syntax */
5176 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
5178 /* Introduce messages they have */
5179 int res;
5181 res = ast_play_and_wait(chan, "vm-youhave");
5182 if (res)
5183 return res;
5185 if (!vms->oldmessages && !vms->newmessages) {
5186 res = ast_play_and_wait(chan, "vm-no");
5187 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5188 return res;
5191 if (vms->newmessages) {
5192 if ((vms->newmessages == 1)) {
5193 res = ast_play_and_wait(chan, "digits/1");
5194 res = res ? res : ast_play_and_wait(chan, "vm-ny");
5195 res = res ? res : ast_play_and_wait(chan, "vm-message");
5196 } else {
5197 res = say_and_wait(chan, vms->newmessages, chan->language);
5198 res = res ? res : ast_play_and_wait(chan, "vm-nye");
5199 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5201 if (!res && vms->oldmessages)
5202 res = ast_play_and_wait(chan, "vm-and");
5204 if (!res && vms->oldmessages) {
5205 if (vms->oldmessages == 1) {
5206 res = ast_play_and_wait(chan, "digits/1");
5207 res = res ? res : ast_play_and_wait(chan, "vm-gamel");
5208 res = res ? res : ast_play_and_wait(chan, "vm-message");
5209 } else {
5210 res = say_and_wait(chan, vms->oldmessages, chan->language);
5211 res = res ? res : ast_play_and_wait(chan, "vm-gamle");
5212 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5216 return res;
5219 /* GERMAN syntax */
5220 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
5222 /* Introduce messages they have */
5223 int res;
5224 res = ast_play_and_wait(chan, "vm-youhave");
5225 if (!res) {
5226 if (vms->newmessages) {
5227 if ((vms->newmessages == 1))
5228 res = ast_play_and_wait(chan, "digits/1F");
5229 else
5230 res = say_and_wait(chan, vms->newmessages, chan->language);
5231 if (!res)
5232 res = ast_play_and_wait(chan, "vm-INBOX");
5233 if (vms->oldmessages && !res)
5234 res = ast_play_and_wait(chan, "vm-and");
5235 else if (!res) {
5236 if ((vms->newmessages == 1))
5237 res = ast_play_and_wait(chan, "vm-message");
5238 else
5239 res = ast_play_and_wait(chan, "vm-messages");
5243 if (!res && vms->oldmessages) {
5244 if (vms->oldmessages == 1)
5245 res = ast_play_and_wait(chan, "digits/1F");
5246 else
5247 res = say_and_wait(chan, vms->oldmessages, chan->language);
5248 if (!res)
5249 res = ast_play_and_wait(chan, "vm-Old");
5250 if (!res) {
5251 if (vms->oldmessages == 1)
5252 res = ast_play_and_wait(chan, "vm-message");
5253 else
5254 res = ast_play_and_wait(chan, "vm-messages");
5257 if (!res) {
5258 if (!vms->oldmessages && !vms->newmessages) {
5259 res = ast_play_and_wait(chan, "vm-no");
5260 if (!res)
5261 res = ast_play_and_wait(chan, "vm-messages");
5265 return res;
5268 /* SPANISH syntax */
5269 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
5271 /* Introduce messages they have */
5272 int res;
5273 if (!vms->oldmessages && !vms->newmessages) {
5274 res = ast_play_and_wait(chan, "vm-youhaveno");
5275 if (!res)
5276 res = ast_play_and_wait(chan, "vm-messages");
5277 } else {
5278 res = ast_play_and_wait(chan, "vm-youhave");
5280 if (!res) {
5281 if (vms->newmessages) {
5282 if (!res) {
5283 if ((vms->newmessages == 1)) {
5284 res = ast_play_and_wait(chan, "digits/1M");
5285 if (!res)
5286 res = ast_play_and_wait(chan, "vm-message");
5287 if (!res)
5288 res = ast_play_and_wait(chan, "vm-INBOXs");
5289 } else {
5290 res = say_and_wait(chan, vms->newmessages, chan->language);
5291 if (!res)
5292 res = ast_play_and_wait(chan, "vm-messages");
5293 if (!res)
5294 res = ast_play_and_wait(chan, "vm-INBOX");
5297 if (vms->oldmessages && !res)
5298 res = ast_play_and_wait(chan, "vm-and");
5300 if (vms->oldmessages) {
5301 if (!res) {
5302 if (vms->oldmessages == 1) {
5303 res = ast_play_and_wait(chan, "digits/1M");
5304 if (!res)
5305 res = ast_play_and_wait(chan, "vm-message");
5306 if (!res)
5307 res = ast_play_and_wait(chan, "vm-Olds");
5308 } else {
5309 res = say_and_wait(chan, vms->oldmessages, chan->language);
5310 if (!res)
5311 res = ast_play_and_wait(chan, "vm-messages");
5312 if (!res)
5313 res = ast_play_and_wait(chan, "vm-Old");
5318 return res;
5321 /* BRAZILIAN PORTUGUESE syntax */
5322 static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
5323 /* Introduce messages they have */
5324 int res;
5325 if (!vms->oldmessages && !vms->newmessages) {
5326 res = ast_play_and_wait(chan, "vm-nomessages");
5327 return res;
5329 else {
5330 res = ast_play_and_wait(chan, "vm-youhave");
5332 if (vms->newmessages) {
5333 if (!res)
5334 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
5335 if ((vms->newmessages == 1)) {
5336 if (!res)
5337 res = ast_play_and_wait(chan, "vm-message");
5338 if (!res)
5339 res = ast_play_and_wait(chan, "vm-INBOXs");
5341 else {
5342 if (!res)
5343 res = ast_play_and_wait(chan, "vm-messages");
5344 if (!res)
5345 res = ast_play_and_wait(chan, "vm-INBOX");
5347 if (vms->oldmessages && !res)
5348 res = ast_play_and_wait(chan, "vm-and");
5350 if (vms->oldmessages) {
5351 if (!res)
5352 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
5353 if (vms->oldmessages == 1) {
5354 if (!res)
5355 res = ast_play_and_wait(chan, "vm-message");
5356 if (!res)
5357 res = ast_play_and_wait(chan, "vm-Olds");
5359 else {
5360 if (!res)
5361 res = ast_play_and_wait(chan, "vm-messages");
5362 if (!res)
5363 res = ast_play_and_wait(chan, "vm-Old");
5366 return res;
5369 /* FRENCH syntax */
5370 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
5372 /* Introduce messages they have */
5373 int res;
5374 res = ast_play_and_wait(chan, "vm-youhave");
5375 if (!res) {
5376 if (vms->newmessages) {
5377 res = say_and_wait(chan, vms->newmessages, chan->language);
5378 if (!res)
5379 res = ast_play_and_wait(chan, "vm-INBOX");
5380 if (vms->oldmessages && !res)
5381 res = ast_play_and_wait(chan, "vm-and");
5382 else if (!res) {
5383 if ((vms->newmessages == 1))
5384 res = ast_play_and_wait(chan, "vm-message");
5385 else
5386 res = ast_play_and_wait(chan, "vm-messages");
5390 if (!res && vms->oldmessages) {
5391 res = say_and_wait(chan, vms->oldmessages, chan->language);
5392 if (!res)
5393 res = ast_play_and_wait(chan, "vm-Old");
5394 if (!res) {
5395 if (vms->oldmessages == 1)
5396 res = ast_play_and_wait(chan, "vm-message");
5397 else
5398 res = ast_play_and_wait(chan, "vm-messages");
5401 if (!res) {
5402 if (!vms->oldmessages && !vms->newmessages) {
5403 res = ast_play_and_wait(chan, "vm-no");
5404 if (!res)
5405 res = ast_play_and_wait(chan, "vm-messages");
5409 return res;
5412 /* DUTCH syntax */
5413 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
5415 /* Introduce messages they have */
5416 int res;
5417 res = ast_play_and_wait(chan, "vm-youhave");
5418 if (!res) {
5419 if (vms->newmessages) {
5420 res = say_and_wait(chan, vms->newmessages, chan->language);
5421 if (!res) {
5422 if (vms->newmessages == 1)
5423 res = ast_play_and_wait(chan, "vm-INBOXs");
5424 else
5425 res = ast_play_and_wait(chan, "vm-INBOX");
5427 if (vms->oldmessages && !res)
5428 res = ast_play_and_wait(chan, "vm-and");
5429 else if (!res) {
5430 if ((vms->newmessages == 1))
5431 res = ast_play_and_wait(chan, "vm-message");
5432 else
5433 res = ast_play_and_wait(chan, "vm-messages");
5437 if (!res && vms->oldmessages) {
5438 res = say_and_wait(chan, vms->oldmessages, chan->language);
5439 if (!res) {
5440 if (vms->oldmessages == 1)
5441 res = ast_play_and_wait(chan, "vm-Olds");
5442 else
5443 res = ast_play_and_wait(chan, "vm-Old");
5445 if (!res) {
5446 if (vms->oldmessages == 1)
5447 res = ast_play_and_wait(chan, "vm-message");
5448 else
5449 res = ast_play_and_wait(chan, "vm-messages");
5452 if (!res) {
5453 if (!vms->oldmessages && !vms->newmessages) {
5454 res = ast_play_and_wait(chan, "vm-no");
5455 if (!res)
5456 res = ast_play_and_wait(chan, "vm-messages");
5460 return res;
5463 /* PORTUGUESE syntax */
5464 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
5466 /* Introduce messages they have */
5467 int res;
5468 res = ast_play_and_wait(chan, "vm-youhave");
5469 if (!res) {
5470 if (vms->newmessages) {
5471 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
5472 if (!res) {
5473 if ((vms->newmessages == 1)) {
5474 res = ast_play_and_wait(chan, "vm-message");
5475 if (!res)
5476 res = ast_play_and_wait(chan, "vm-INBOXs");
5477 } else {
5478 res = ast_play_and_wait(chan, "vm-messages");
5479 if (!res)
5480 res = ast_play_and_wait(chan, "vm-INBOX");
5483 if (vms->oldmessages && !res)
5484 res = ast_play_and_wait(chan, "vm-and");
5486 if (!res && vms->oldmessages) {
5487 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
5488 if (!res) {
5489 if (vms->oldmessages == 1) {
5490 res = ast_play_and_wait(chan, "vm-message");
5491 if (!res)
5492 res = ast_play_and_wait(chan, "vm-Olds");
5493 } else {
5494 res = ast_play_and_wait(chan, "vm-messages");
5495 if (!res)
5496 res = ast_play_and_wait(chan, "vm-Old");
5500 if (!res) {
5501 if (!vms->oldmessages && !vms->newmessages) {
5502 res = ast_play_and_wait(chan, "vm-no");
5503 if (!res)
5504 res = ast_play_and_wait(chan, "vm-messages");
5508 return res;
5512 /* CZECH syntax */
5513 /* in czech there must be declension of word new and message
5514 * czech : english : czech : english
5515 * --------------------------------------------------------
5516 * vm-youhave : you have
5517 * vm-novou : one new : vm-zpravu : message
5518 * vm-nove : 2-4 new : vm-zpravy : messages
5519 * vm-novych : 5-infinite new : vm-zprav : messages
5520 * vm-starou : one old
5521 * vm-stare : 2-4 old
5522 * vm-starych : 5-infinite old
5523 * jednu : one - falling 4.
5524 * vm-no : no ( no messages )
5527 static int vm_intro_cz(struct ast_channel *chan,struct vm_state *vms)
5529 int res;
5530 res = ast_play_and_wait(chan, "vm-youhave");
5531 if (!res) {
5532 if (vms->newmessages) {
5533 if (vms->newmessages == 1) {
5534 res = ast_play_and_wait(chan, "digits/jednu");
5535 } else {
5536 res = say_and_wait(chan, vms->newmessages, chan->language);
5538 if (!res) {
5539 if ((vms->newmessages == 1))
5540 res = ast_play_and_wait(chan, "vm-novou");
5541 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
5542 res = ast_play_and_wait(chan, "vm-nove");
5543 if (vms->newmessages > 4)
5544 res = ast_play_and_wait(chan, "vm-novych");
5546 if (vms->oldmessages && !res)
5547 res = ast_play_and_wait(chan, "vm-and");
5548 else if (!res) {
5549 if ((vms->newmessages == 1))
5550 res = ast_play_and_wait(chan, "vm-zpravu");
5551 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
5552 res = ast_play_and_wait(chan, "vm-zpravy");
5553 if (vms->newmessages > 4)
5554 res = ast_play_and_wait(chan, "vm-zprav");
5557 if (!res && vms->oldmessages) {
5558 res = say_and_wait(chan, vms->oldmessages, chan->language);
5559 if (!res) {
5560 if ((vms->oldmessages == 1))
5561 res = ast_play_and_wait(chan, "vm-starou");
5562 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
5563 res = ast_play_and_wait(chan, "vm-stare");
5564 if (vms->oldmessages > 4)
5565 res = ast_play_and_wait(chan, "vm-starych");
5567 if (!res) {
5568 if ((vms->oldmessages == 1))
5569 res = ast_play_and_wait(chan, "vm-zpravu");
5570 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
5571 res = ast_play_and_wait(chan, "vm-zpravy");
5572 if (vms->oldmessages > 4)
5573 res = ast_play_and_wait(chan, "vm-zprav");
5576 if (!res) {
5577 if (!vms->oldmessages && !vms->newmessages) {
5578 res = ast_play_and_wait(chan, "vm-no");
5579 if (!res)
5580 res = ast_play_and_wait(chan, "vm-zpravy");
5584 return res;
5587 static int get_lastdigits(int num)
5589 num %= 100;
5590 return (num < 20) ? num : num % 10;
5593 static int vm_intro_ru(struct ast_channel *chan,struct vm_state *vms)
5595 int res;
5596 int lastnum = 0;
5597 int dcnum;
5599 res = ast_play_and_wait(chan, "vm-youhave");
5600 if (!res && vms->newmessages) {
5601 lastnum = get_lastdigits(vms->newmessages);
5602 dcnum = vms->newmessages - lastnum;
5603 if (dcnum)
5604 res = say_and_wait(chan, dcnum, chan->language);
5605 if (!res && lastnum) {
5606 if (lastnum == 1)
5607 res = ast_play_and_wait(chan, "digits/ru/odno");
5608 else
5609 res = say_and_wait(chan, lastnum, chan->language);
5612 if (!res)
5613 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-novoe" : "vm-novyh");
5615 if (!res && vms->oldmessages)
5616 res = ast_play_and_wait(chan, "vm-and");
5619 if (!res && vms->oldmessages) {
5620 lastnum = get_lastdigits(vms->oldmessages);
5621 dcnum = vms->oldmessages - lastnum;
5622 if (dcnum)
5623 res = say_and_wait(chan, dcnum, chan->language);
5624 if (!res && lastnum) {
5625 if (lastnum == 1)
5626 res = ast_play_and_wait(chan, "digits/ru/odno");
5627 else
5628 res = say_and_wait(chan, lastnum, chan->language);
5631 if (!res)
5632 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-staroe" : "vm-staryh");
5635 if (!res && !vms->newmessages && !vms->oldmessages) {
5636 lastnum = 0;
5637 res = ast_play_and_wait(chan, "vm-no");
5640 if (!res) {
5641 switch (lastnum) {
5642 case 1:
5643 res = ast_play_and_wait(chan, "vm-soobshenie");
5644 break;
5645 case 2:
5646 case 3:
5647 case 4:
5648 res = ast_play_and_wait(chan, "vm-soobsheniya");
5649 break;
5650 default:
5651 res = ast_play_and_wait(chan, "vm-soobsheniy");
5652 break;
5656 return res;
5659 /* UKRAINIAN syntax */
5660 /* in ukrainian the syntax is different so we need the following files
5661 * --------------------------------------------------------
5662 * /digits/ua/1e 'odne'
5663 * vm-nove 'nove'
5664 * vm-stare 'stare'
5667 static int vm_intro_ua(struct ast_channel *chan,struct vm_state *vms)
5669 int res;
5670 int lastnum = 0;
5671 int dcnum;
5673 res = ast_play_and_wait(chan, "vm-youhave");
5674 if (!res && vms->newmessages) {
5675 lastnum = get_lastdigits(vms->newmessages);
5676 dcnum = vms->newmessages - lastnum;
5677 if (dcnum)
5678 res = say_and_wait(chan, dcnum, chan->language);
5679 if (!res && lastnum) {
5680 if (lastnum == 1)
5681 res = ast_play_and_wait(chan, "digits/ua/1e");
5682 else
5683 res = say_and_wait(chan, lastnum, chan->language);
5686 if (!res)
5687 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-nove" : "vm-INBOX");
5689 if (!res && vms->oldmessages)
5690 res = ast_play_and_wait(chan, "vm-and");
5693 if (!res && vms->oldmessages) {
5694 lastnum = get_lastdigits(vms->oldmessages);
5695 dcnum = vms->oldmessages - lastnum;
5696 if (dcnum)
5697 res = say_and_wait(chan, dcnum, chan->language);
5698 if (!res && lastnum) {
5699 if (lastnum == 1)
5700 res = ast_play_and_wait(chan, "digits/ua/1e");
5701 else
5702 res = say_and_wait(chan, lastnum, chan->language);
5705 if (!res)
5706 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-stare" : "vm-Old");
5709 if (!res && !vms->newmessages && !vms->oldmessages) {
5710 lastnum = 0;
5711 res = ast_play_and_wait(chan, "vm-no");
5714 if (!res) {
5715 switch (lastnum) {
5716 case 1:
5717 case 2:
5718 case 3:
5719 case 4:
5720 res = ast_play_and_wait(chan, "vm-message");
5721 break;
5722 default:
5723 res = ast_play_and_wait(chan, "vm-messages");
5724 break;
5728 return res;
5732 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
5734 char prefile[256];
5736 /* Notify the user that the temp greeting is set and give them the option to remove it */
5737 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
5738 if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
5739 if (ast_fileexists(prefile, NULL, NULL) > 0)
5740 ast_play_and_wait(chan, "vm-tempgreetactive");
5743 /* Play voicemail intro - syntax is different for different languages */
5744 if (!strcasecmp(chan->language, "de")) { /* GERMAN syntax */
5745 return vm_intro_de(chan, vms);
5746 } else if (!strcasecmp(chan->language, "es")) { /* SPANISH syntax */
5747 return vm_intro_es(chan, vms);
5748 } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN syntax */
5749 return vm_intro_it(chan, vms);
5750 } else if (!strcasecmp(chan->language, "fr")) { /* FRENCH syntax */
5751 return vm_intro_fr(chan, vms);
5752 } else if (!strcasecmp(chan->language, "nl")) { /* DUTCH syntax */
5753 return vm_intro_nl(chan, vms);
5754 } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE syntax */
5755 return vm_intro_pt(chan, vms);
5756 } else if (!strcasecmp(chan->language, "pt_BR")) { /* BRAZILIAN PORTUGUESE syntax */
5757 return vm_intro_pt_BR(chan, vms);
5758 } else if (!strcasecmp(chan->language, "cz")) { /* CZECH syntax */
5759 return vm_intro_cz(chan, vms);
5760 } else if (!strcasecmp(chan->language, "gr")) { /* GREEK syntax */
5761 return vm_intro_gr(chan, vms);
5762 } else if (!strcasecmp(chan->language, "pl")) { /* POLISH syntax */
5763 return vm_intro_pl(chan, vms);
5764 } else if (!strcasecmp(chan->language, "se")) { /* SWEDISH syntax */
5765 return vm_intro_se(chan, vms);
5766 } else if (!strcasecmp(chan->language, "no")) { /* NORWEGIAN syntax */
5767 return vm_intro_no(chan, vms);
5768 } else if (!strcasecmp(chan->language, "ru")) { /* RUSSIAN syntax */
5769 return vm_intro_ru(chan, vms);
5770 } else if (!strcasecmp(chan->language, "ua")) { /* UKRAINIAN syntax */
5771 return vm_intro_ua(chan, vms);
5772 } else { /* Default to ENGLISH */
5773 return vm_intro_en(chan, vms);
5777 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
5779 int res = 0;
5780 /* Play instructions and wait for new command */
5781 while (!res) {
5782 if (vms->starting) {
5783 if (vms->lastmsg > -1) {
5784 res = ast_play_and_wait(chan, "vm-onefor");
5785 if (!res)
5786 res = vm_play_folder_name(chan, vms->vmbox);
5788 if (!res)
5789 res = ast_play_and_wait(chan, "vm-opts");
5790 } else {
5791 if (vms->curmsg)
5792 res = ast_play_and_wait(chan, "vm-prev");
5793 if (!res && !skipadvanced)
5794 res = ast_play_and_wait(chan, "vm-advopts");
5795 if (!res)
5796 res = ast_play_and_wait(chan, "vm-repeat");
5797 if (!res && (vms->curmsg != vms->lastmsg))
5798 res = ast_play_and_wait(chan, "vm-next");
5799 if (!res) {
5800 if (!vms->deleted[vms->curmsg])
5801 res = ast_play_and_wait(chan, "vm-delete");
5802 else
5803 res = ast_play_and_wait(chan, "vm-undelete");
5804 if (!res)
5805 res = ast_play_and_wait(chan, "vm-toforward");
5806 if (!res)
5807 res = ast_play_and_wait(chan, "vm-savemessage");
5810 if (!res)
5811 res = ast_play_and_wait(chan, "vm-helpexit");
5812 if (!res)
5813 res = ast_waitfordigit(chan, 6000);
5814 if (!res) {
5815 vms->repeats++;
5816 if (vms->repeats > 2) {
5817 res = 't';
5821 return res;
5824 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
5826 int cmd = 0;
5827 int duration = 0;
5828 int tries = 0;
5829 char newpassword[80] = "";
5830 char newpassword2[80] = "";
5831 char prefile[PATH_MAX] = "";
5832 unsigned char buf[256];
5833 int bytes=0;
5835 if (ast_adsi_available(chan)) {
5836 bytes += adsi_logo(buf + bytes);
5837 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
5838 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
5839 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5840 bytes += ast_adsi_voice_mode(buf + bytes, 0);
5841 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5844 /* First, have the user change their password
5845 so they won't get here again */
5846 for (;;) {
5847 newpassword[1] = '\0';
5848 newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
5849 if (cmd == '#')
5850 newpassword[0] = '\0';
5851 if (cmd < 0 || cmd == 't' || cmd == '#')
5852 return cmd;
5853 cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
5854 if (cmd < 0 || cmd == 't' || cmd == '#')
5855 return cmd;
5856 newpassword2[1] = '\0';
5857 newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
5858 if (cmd == '#')
5859 newpassword2[0] = '\0';
5860 if (cmd < 0 || cmd == 't' || cmd == '#')
5861 return cmd;
5862 cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#");
5863 if (cmd < 0 || cmd == 't' || cmd == '#')
5864 return cmd;
5865 if (!strcmp(newpassword, newpassword2))
5866 break;
5867 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
5868 cmd = ast_play_and_wait(chan, "vm-mismatch");
5869 if (++tries == 3)
5870 return -1;
5872 if (ast_strlen_zero(ext_pass_cmd))
5873 vm_change_password(vmu,newpassword);
5874 else
5875 vm_change_password_shell(vmu,newpassword);
5876 if (option_debug > 2)
5877 ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
5878 cmd = ast_play_and_wait(chan,"vm-passchanged");
5880 /* If forcename is set, have the user record their name */
5881 if (ast_test_flag(vmu, VM_FORCENAME)) {
5882 snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
5883 if (ast_fileexists(prefile, NULL, NULL) < 1) {
5884 #ifndef IMAP_STORAGE
5885 cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
5886 #else
5887 cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
5888 #endif
5889 if (cmd < 0 || cmd == 't' || cmd == '#')
5890 return cmd;
5894 /* If forcegreetings is set, have the user record their greetings */
5895 if (ast_test_flag(vmu, VM_FORCEGREET)) {
5896 snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
5897 if (ast_fileexists(prefile, NULL, NULL) < 1) {
5898 #ifndef IMAP_STORAGE
5899 cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
5900 #else
5901 cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
5902 #endif
5903 if (cmd < 0 || cmd == 't' || cmd == '#')
5904 return cmd;
5907 snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
5908 if (ast_fileexists(prefile, NULL, NULL) < 1) {
5909 #ifndef IMAP_STORAGE
5910 cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
5911 #else
5912 cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
5913 #endif
5914 if (cmd < 0 || cmd == 't' || cmd == '#')
5915 return cmd;
5919 return cmd;
5922 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
5924 int cmd = 0;
5925 int retries = 0;
5926 int duration = 0;
5927 char newpassword[80] = "";
5928 char newpassword2[80] = "";
5929 char prefile[PATH_MAX] = "";
5930 unsigned char buf[256];
5931 int bytes=0;
5933 if (ast_adsi_available(chan))
5935 bytes += adsi_logo(buf + bytes);
5936 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
5937 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
5938 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5939 bytes += ast_adsi_voice_mode(buf + bytes, 0);
5940 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5942 while ((cmd >= 0) && (cmd != 't')) {
5943 if (cmd)
5944 retries = 0;
5945 switch (cmd) {
5946 case '1':
5947 snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
5948 #ifndef IMAP_STORAGE
5949 cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
5950 #else
5951 cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
5952 #endif
5953 break;
5954 case '2':
5955 snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
5956 #ifndef IMAP_STORAGE
5957 cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
5958 #else
5959 cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
5960 #endif
5961 break;
5962 case '3':
5963 snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
5964 #ifndef IMAP_STORAGE
5965 cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
5966 #else
5967 cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
5968 #endif
5969 break;
5970 case '4':
5971 cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
5972 break;
5973 case '5':
5974 if (vmu->password[0] == '-') {
5975 cmd = ast_play_and_wait(chan, "vm-no");
5976 break;
5978 newpassword[1] = '\0';
5979 newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
5980 if (cmd == '#')
5981 newpassword[0] = '\0';
5982 else {
5983 if (cmd < 0)
5984 break;
5985 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
5986 break;
5989 newpassword2[1] = '\0';
5990 newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
5991 if (cmd == '#')
5992 newpassword2[0] = '\0';
5993 else {
5994 if (cmd < 0)
5995 break;
5997 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
5998 break;
6001 if (strcmp(newpassword, newpassword2)) {
6002 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
6003 cmd = ast_play_and_wait(chan, "vm-mismatch");
6004 break;
6006 if (ast_strlen_zero(ext_pass_cmd))
6007 vm_change_password(vmu,newpassword);
6008 else
6009 vm_change_password_shell(vmu,newpassword);
6010 if (option_debug > 2)
6011 ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
6012 cmd = ast_play_and_wait(chan,"vm-passchanged");
6013 break;
6014 case '*':
6015 cmd = 't';
6016 break;
6017 default:
6018 cmd = ast_play_and_wait(chan,"vm-options");
6019 if (!cmd)
6020 cmd = ast_waitfordigit(chan,6000);
6021 if (!cmd)
6022 retries++;
6023 if (retries > 3)
6024 cmd = 't';
6027 if (cmd == 't')
6028 cmd = 0;
6029 return cmd;
6032 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
6034 int res;
6035 int cmd = 0;
6036 int retries = 0;
6037 int duration = 0;
6038 char prefile[PATH_MAX] = "";
6039 unsigned char buf[256];
6040 char dest[PATH_MAX];
6041 int bytes = 0;
6043 if (ast_adsi_available(chan)) {
6044 bytes += adsi_logo(buf + bytes);
6045 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
6046 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
6047 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6048 bytes += ast_adsi_voice_mode(buf + bytes, 0);
6049 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6052 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
6053 if ((res = create_dirpath(dest, sizeof(dest), vmu->context, vms->username, "temp"))) {
6054 ast_log(LOG_WARNING, "Failed to create directory (%s).\n", prefile);
6055 return -1;
6057 while((cmd >= 0) && (cmd != 't')) {
6058 if (cmd)
6059 retries = 0;
6060 RETRIEVE(prefile, -1);
6061 if (ast_fileexists(prefile, NULL, NULL) <= 0) {
6062 #ifndef IMAP_STORAGE
6063 play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6064 #else
6065 play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6066 #endif
6067 cmd = 't';
6068 } else {
6069 switch (cmd) {
6070 case '1':
6071 #ifndef IMAP_STORAGE
6072 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6073 #else
6074 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6075 #endif
6076 break;
6077 case '2':
6078 DELETE(prefile, -1, prefile);
6079 ast_play_and_wait(chan, "vm-tempremoved");
6080 cmd = 't';
6081 break;
6082 case '*':
6083 cmd = 't';
6084 break;
6085 default:
6086 cmd = ast_play_and_wait(chan,
6087 ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
6088 "vm-tempgreeting2" : "vm-tempgreeting");
6089 if (!cmd)
6090 cmd = ast_waitfordigit(chan,6000);
6091 if (!cmd)
6092 retries++;
6093 if (retries > 3)
6094 cmd = 't';
6097 DISPOSE(prefile, -1);
6099 if (cmd == 't')
6100 cmd = 0;
6101 return cmd;
6104 /* GREEK SYNTAX */
6106 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6108 int cmd=0;
6110 if (vms->lastmsg > -1) {
6111 cmd = play_message(chan, vmu, vms);
6112 } else {
6113 cmd = ast_play_and_wait(chan, "vm-youhaveno");
6114 if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
6115 if (!cmd) {
6116 snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
6117 cmd = ast_play_and_wait(chan, vms->fn);
6119 if (!cmd)
6120 cmd = ast_play_and_wait(chan, "vm-messages");
6121 } else {
6122 if (!cmd)
6123 cmd = ast_play_and_wait(chan, "vm-messages");
6124 if (!cmd) {
6125 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6126 cmd = ast_play_and_wait(chan, vms->fn);
6130 return cmd;
6133 /* Default English syntax */
6134 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6136 int cmd=0;
6138 if (vms->lastmsg > -1) {
6139 cmd = play_message(chan, vmu, vms);
6140 } else {
6141 cmd = ast_play_and_wait(chan, "vm-youhave");
6142 if (!cmd)
6143 cmd = ast_play_and_wait(chan, "vm-no");
6144 if (!cmd) {
6145 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6146 cmd = ast_play_and_wait(chan, vms->fn);
6148 if (!cmd)
6149 cmd = ast_play_and_wait(chan, "vm-messages");
6151 return cmd;
6154 /* ITALIAN syntax */
6155 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6157 int cmd=0;
6159 if (vms->lastmsg > -1) {
6160 cmd = play_message(chan, vmu, vms);
6161 } else {
6162 cmd = ast_play_and_wait(chan, "vm-no");
6163 if (!cmd)
6164 cmd = ast_play_and_wait(chan, "vm-message");
6165 if (!cmd) {
6166 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6167 cmd = ast_play_and_wait(chan, vms->fn);
6170 return cmd;
6173 /* SPANISH syntax */
6174 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6176 int cmd=0;
6178 if (vms->lastmsg > -1) {
6179 cmd = play_message(chan, vmu, vms);
6180 } else {
6181 cmd = ast_play_and_wait(chan, "vm-youhaveno");
6182 if (!cmd)
6183 cmd = ast_play_and_wait(chan, "vm-messages");
6184 if (!cmd) {
6185 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6186 cmd = ast_play_and_wait(chan, vms->fn);
6189 return cmd;
6192 /* PORTUGUESE syntax */
6193 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6195 int cmd=0;
6197 if (vms->lastmsg > -1) {
6198 cmd = play_message(chan, vmu, vms);
6199 } else {
6200 cmd = ast_play_and_wait(chan, "vm-no");
6201 if (!cmd) {
6202 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6203 cmd = ast_play_and_wait(chan, vms->fn);
6205 if (!cmd)
6206 cmd = ast_play_and_wait(chan, "vm-messages");
6208 return cmd;
6211 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6213 if (!strcasecmp(chan->language, "es")) { /* SPANISH */
6214 return vm_browse_messages_es(chan, vms, vmu);
6215 } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN */
6216 return vm_browse_messages_it(chan, vms, vmu);
6217 } else if (!strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* PORTUGUESE */
6218 return vm_browse_messages_pt(chan, vms, vmu);
6219 } else if (!strcasecmp(chan->language, "gr")){
6220 return vm_browse_messages_gr(chan, vms, vmu); /* GREEK */
6221 } else { /* Default to English syntax */
6222 return vm_browse_messages_en(chan, vms, vmu);
6226 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
6227 struct ast_vm_user *res_vmu, const char *context, const char *prefix,
6228 int skipuser, int maxlogins, int silent)
6230 int useadsi=0, valid=0, logretries=0;
6231 char password[AST_MAX_EXTENSION]="", *passptr;
6232 struct ast_vm_user vmus, *vmu = NULL;
6234 /* If ADSI is supported, setup login screen */
6235 adsi_begin(chan, &useadsi);
6236 if (!skipuser && useadsi)
6237 adsi_login(chan);
6238 if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
6239 ast_log(LOG_WARNING, "Couldn't stream login file\n");
6240 return -1;
6243 /* Authenticate them and get their mailbox/password */
6245 while (!valid && (logretries < maxlogins)) {
6246 /* Prompt for, and read in the username */
6247 if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
6248 ast_log(LOG_WARNING, "Couldn't read username\n");
6249 return -1;
6251 if (ast_strlen_zero(mailbox)) {
6252 if (chan->cid.cid_num) {
6253 ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
6254 } else {
6255 if (option_verbose > 2)
6256 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
6257 return -1;
6260 if (useadsi)
6261 adsi_password(chan);
6263 if (!ast_strlen_zero(prefix)) {
6264 char fullusername[80] = "";
6265 ast_copy_string(fullusername, prefix, sizeof(fullusername));
6266 strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
6267 ast_copy_string(mailbox, fullusername, mailbox_size);
6270 if (option_debug)
6271 ast_log(LOG_DEBUG, "Before find user for mailbox %s\n",mailbox);
6272 vmu = find_user(&vmus, context, mailbox);
6273 if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
6274 /* saved password is blank, so don't bother asking */
6275 password[0] = '\0';
6276 } else {
6277 if (ast_streamfile(chan, "vm-password", chan->language)) {
6278 ast_log(LOG_WARNING, "Unable to stream password file\n");
6279 return -1;
6281 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
6282 ast_log(LOG_WARNING, "Unable to read password\n");
6283 return -1;
6287 if (vmu) {
6288 passptr = vmu->password;
6289 if (passptr[0] == '-') passptr++;
6291 if (vmu && !strcmp(passptr, password))
6292 valid++;
6293 else {
6294 if (option_verbose > 2)
6295 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
6296 if (!ast_strlen_zero(prefix))
6297 mailbox[0] = '\0';
6299 logretries++;
6300 if (!valid) {
6301 if (skipuser || logretries >= maxlogins) {
6302 if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
6303 ast_log(LOG_WARNING, "Unable to stream incorrect message\n");
6304 return -1;
6306 } else {
6307 if (useadsi)
6308 adsi_login(chan);
6309 if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
6310 ast_log(LOG_WARNING, "Unable to stream incorrect mailbox message\n");
6311 return -1;
6314 if (ast_waitstream(chan, "")) /* Channel is hung up */
6315 return -1;
6318 if (!valid && (logretries >= maxlogins)) {
6319 ast_stopstream(chan);
6320 ast_play_and_wait(chan, "vm-goodbye");
6321 return -1;
6323 if (vmu && !skipuser) {
6324 memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
6326 return 0;
6329 static int vm_execmain(struct ast_channel *chan, void *data)
6331 /* XXX This is, admittedly, some pretty horrendus code. For some
6332 reason it just seemed a lot easier to do with GOTO's. I feel
6333 like I'm back in my GWBASIC days. XXX */
6334 int res=-1;
6335 int cmd=0;
6336 int valid = 0;
6337 struct ast_module_user *u;
6338 char prefixstr[80] ="";
6339 char ext_context[256]="";
6340 int box;
6341 int useadsi = 0;
6342 int skipuser = 0;
6343 struct vm_state vms;
6344 struct ast_vm_user *vmu = NULL, vmus;
6345 char *context=NULL;
6346 int silentexit = 0;
6347 struct ast_flags flags = { 0 };
6348 signed char record_gain = 0;
6349 int play_auto = 0;
6350 int play_folder = 0;
6351 #ifdef IMAP_STORAGE
6352 int deleted = 0;
6353 #endif
6354 u = ast_module_user_add(chan);
6356 /* Add the vm_state to the active list and keep it active */
6357 memset(&vms, 0, sizeof(vms));
6358 vms.lastmsg = -1;
6360 memset(&vmus, 0, sizeof(vmus));
6362 if (chan->_state != AST_STATE_UP) {
6363 if (option_debug)
6364 ast_log(LOG_DEBUG, "Before ast_answer\n");
6365 ast_answer(chan);
6368 if (!ast_strlen_zero(data)) {
6369 char *opts[OPT_ARG_ARRAY_SIZE];
6370 char *parse;
6371 AST_DECLARE_APP_ARGS(args,
6372 AST_APP_ARG(argv0);
6373 AST_APP_ARG(argv1);
6376 parse = ast_strdupa(data);
6378 AST_STANDARD_APP_ARGS(args, parse);
6380 if (args.argc == 2) {
6381 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
6382 ast_module_user_remove(u);
6383 return -1;
6385 if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
6386 int gain;
6387 if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
6388 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
6389 ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
6390 ast_module_user_remove(u);
6391 return -1;
6392 } else {
6393 record_gain = (signed char) gain;
6395 } else {
6396 ast_log(LOG_WARNING, "Invalid Gain level set with option g\n");
6399 if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
6400 play_auto = 1;
6401 if (opts[OPT_ARG_PLAYFOLDER]) {
6402 if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%d", &play_folder) != 1) {
6403 ast_log(LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
6405 } else {
6406 ast_log(LOG_WARNING, "Invalid folder set with option a\n");
6408 if ( play_folder > 9 || play_folder < 0) {
6409 ast_log(LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
6410 play_folder = 0;
6413 } else {
6414 /* old style options parsing */
6415 while (*(args.argv0)) {
6416 if (*(args.argv0) == 's')
6417 ast_set_flag(&flags, OPT_SILENT);
6418 else if (*(args.argv0) == 'p')
6419 ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
6420 else
6421 break;
6422 (args.argv0)++;
6427 valid = ast_test_flag(&flags, OPT_SILENT);
6429 if ((context = strchr(args.argv0, '@')))
6430 *context++ = '\0';
6432 if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
6433 ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
6434 else
6435 ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
6437 if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
6438 skipuser++;
6439 else
6440 valid = 0;
6443 if (!valid)
6444 res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
6446 if (option_debug)
6447 ast_log(LOG_DEBUG, "After vm_authenticate\n");
6448 if (!res) {
6449 valid = 1;
6450 if (!skipuser)
6451 vmu = &vmus;
6452 } else {
6453 res = 0;
6456 /* If ADSI is supported, setup login screen */
6457 adsi_begin(chan, &useadsi);
6459 #ifdef IMAP_STORAGE
6460 vms.interactive = 1;
6461 vms.updated = 1;
6462 vmstate_insert(&vms);
6463 init_vm_state(&vms);
6464 #endif
6465 if (!valid)
6466 goto out;
6468 if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
6469 /* TODO: Handle memory allocation failure */
6471 if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
6472 /* TODO: Handle memory allocation failure */
6475 /* Set language from config to override channel language */
6476 if (!ast_strlen_zero(vmu->language))
6477 ast_string_field_set(chan, language, vmu->language);
6478 #ifndef IMAP_STORAGE
6479 create_dirpath(vms.curdir, sizeof(vms.curdir), vmu->context, vms.username, "");
6480 #endif
6481 /* Retrieve old and new message counts */
6482 if (option_debug)
6483 ast_log(LOG_DEBUG, "Before open_mailbox\n");
6484 res = open_mailbox(&vms, vmu, 1);
6485 if (res == ERROR_LOCK_PATH)
6486 goto out;
6487 vms.oldmessages = vms.lastmsg + 1;
6488 if (option_debug > 2)
6489 ast_log(LOG_DEBUG, "Number of old messages: %d\n",vms.oldmessages);
6490 /* Start in INBOX */
6491 res = open_mailbox(&vms, vmu, 0);
6492 if (res == ERROR_LOCK_PATH)
6493 goto out;
6494 vms.newmessages = vms.lastmsg + 1;
6495 if (option_debug > 2)
6496 ast_log(LOG_DEBUG, "Number of new messages: %d\n",vms.newmessages);
6498 /* Select proper mailbox FIRST!! */
6499 if (play_auto) {
6500 res = open_mailbox(&vms, vmu, play_folder);
6501 if (res == ERROR_LOCK_PATH)
6502 goto out;
6504 /* If there are no new messages, inform the user and hangup */
6505 if (vms.lastmsg == -1) {
6506 cmd = vm_browse_messages(chan, &vms, vmu);
6507 res = 0;
6508 goto out;
6510 } else {
6511 if (!vms.newmessages && vms.oldmessages) {
6512 /* If we only have old messages start here */
6513 res = open_mailbox(&vms, vmu, 1);
6514 play_folder = 1;
6515 if (res == ERROR_LOCK_PATH)
6516 goto out;
6520 if (useadsi)
6521 adsi_status(chan, &vms);
6522 res = 0;
6524 /* Check to see if this is a new user */
6525 if (!strcasecmp(vmu->mailbox, vmu->password) &&
6526 (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
6527 if (ast_play_and_wait(chan, "vm-newuser") == -1)
6528 ast_log(LOG_WARNING, "Couldn't stream new user file\n");
6529 cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
6530 if ((cmd == 't') || (cmd == '#')) {
6531 /* Timeout */
6532 res = 0;
6533 goto out;
6534 } else if (cmd < 0) {
6535 /* Hangup */
6536 res = -1;
6537 goto out;
6540 #ifdef IMAP_STORAGE
6541 if(option_debug > 2)
6542 ast_log(LOG_DEBUG, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
6543 if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
6544 if (option_debug)
6545 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!!\n");
6546 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
6548 #endif
6549 if (play_auto) {
6550 cmd = '1';
6551 } else {
6552 cmd = vm_intro(chan, vmu, &vms);
6555 vms.repeats = 0;
6556 vms.starting = 1;
6557 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
6558 /* Run main menu */
6559 switch (cmd) {
6560 case '1':
6561 vms.curmsg = 0;
6562 /* Fall through */
6563 case '5':
6564 cmd = vm_browse_messages(chan, &vms, vmu);
6565 break;
6566 case '2': /* Change folders */
6567 if (useadsi)
6568 adsi_folders(chan, 0, "Change to folder...");
6569 cmd = get_folder2(chan, "vm-changeto", 0);
6570 if (cmd == '#') {
6571 cmd = 0;
6572 } else if (cmd > 0) {
6573 cmd = cmd - '0';
6574 res = close_mailbox(&vms, vmu);
6575 if (res == ERROR_LOCK_PATH)
6576 goto out;
6577 res = open_mailbox(&vms, vmu, cmd);
6578 if (res == ERROR_LOCK_PATH)
6579 goto out;
6580 play_folder = cmd;
6581 cmd = 0;
6583 if (useadsi)
6584 adsi_status2(chan, &vms);
6586 if (!cmd)
6587 cmd = vm_play_folder_name(chan, vms.vmbox);
6589 vms.starting = 1;
6590 break;
6591 case '3': /* Advanced options */
6592 cmd = 0;
6593 vms.repeats = 0;
6594 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
6595 switch (cmd) {
6596 case '1': /* Reply */
6597 if (vms.lastmsg > -1 && !vms.starting) {
6598 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
6599 if (cmd == ERROR_LOCK_PATH) {
6600 res = cmd;
6601 goto out;
6603 } else
6604 cmd = ast_play_and_wait(chan, "vm-sorry");
6605 cmd = 't';
6606 break;
6607 case '2': /* Callback */
6608 if (option_verbose > 2 && !vms.starting)
6609 ast_verbose( VERBOSE_PREFIX_3 "Callback Requested\n");
6610 if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
6611 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
6612 if (cmd == 9) {
6613 silentexit = 1;
6614 goto out;
6615 } else if (cmd == ERROR_LOCK_PATH) {
6616 res = cmd;
6617 goto out;
6620 else
6621 cmd = ast_play_and_wait(chan, "vm-sorry");
6622 cmd = 't';
6623 break;
6624 case '3': /* Envelope */
6625 if (vms.lastmsg > -1 && !vms.starting) {
6626 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
6627 if (cmd == ERROR_LOCK_PATH) {
6628 res = cmd;
6629 goto out;
6631 } else
6632 cmd = ast_play_and_wait(chan, "vm-sorry");
6633 cmd = 't';
6634 break;
6635 case '4': /* Dialout */
6636 if (!ast_strlen_zero(vmu->dialout)) {
6637 cmd = dialout(chan, vmu, NULL, vmu->dialout);
6638 if (cmd == 9) {
6639 silentexit = 1;
6640 goto out;
6643 else
6644 cmd = ast_play_and_wait(chan, "vm-sorry");
6645 cmd = 't';
6646 break;
6648 case '5': /* Leave VoiceMail */
6649 if (ast_test_flag(vmu, VM_SVMAIL)) {
6650 cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain);
6651 if (cmd == ERROR_LOCK_PATH) {
6652 res = cmd;
6653 ast_log(LOG_WARNING, "forward_message failed to lock path.\n");
6654 goto out;
6656 } else
6657 cmd = ast_play_and_wait(chan,"vm-sorry");
6658 cmd='t';
6659 break;
6661 case '*': /* Return to main menu */
6662 cmd = 't';
6663 break;
6665 default:
6666 cmd = 0;
6667 if (!vms.starting) {
6668 cmd = ast_play_and_wait(chan, "vm-toreply");
6670 if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
6671 cmd = ast_play_and_wait(chan, "vm-tocallback");
6673 if (!cmd && !vms.starting) {
6674 cmd = ast_play_and_wait(chan, "vm-tohearenv");
6676 if (!ast_strlen_zero(vmu->dialout) && !cmd) {
6677 cmd = ast_play_and_wait(chan, "vm-tomakecall");
6679 if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
6680 cmd=ast_play_and_wait(chan, "vm-leavemsg");
6681 if (!cmd)
6682 cmd = ast_play_and_wait(chan, "vm-starmain");
6683 if (!cmd)
6684 cmd = ast_waitfordigit(chan,6000);
6685 if (!cmd)
6686 vms.repeats++;
6687 if (vms.repeats > 3)
6688 cmd = 't';
6691 if (cmd == 't') {
6692 cmd = 0;
6693 vms.repeats = 0;
6695 break;
6696 case '4':
6697 if (vms.curmsg > 0) {
6698 vms.curmsg--;
6699 cmd = play_message(chan, vmu, &vms);
6700 } else {
6701 cmd = ast_play_and_wait(chan, "vm-nomore");
6703 break;
6704 case '6':
6705 if (vms.curmsg < vms.lastmsg) {
6706 vms.curmsg++;
6707 cmd = play_message(chan, vmu, &vms);
6708 } else {
6709 cmd = ast_play_and_wait(chan, "vm-nomore");
6711 break;
6712 case '7':
6713 if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
6714 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
6715 if (useadsi)
6716 adsi_delete(chan, &vms);
6717 if (vms.deleted[vms.curmsg]) {
6718 if (play_folder == 0)
6719 vms.newmessages--;
6720 else if (play_folder == 1)
6721 vms.oldmessages--;
6722 cmd = ast_play_and_wait(chan, "vm-deleted");
6724 else {
6725 if (play_folder == 0)
6726 vms.newmessages++;
6727 else if (play_folder == 1)
6728 vms.oldmessages++;
6729 cmd = ast_play_and_wait(chan, "vm-undeleted");
6731 if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
6732 if (vms.curmsg < vms.lastmsg) {
6733 vms.curmsg++;
6734 cmd = play_message(chan, vmu, &vms);
6735 } else {
6736 cmd = ast_play_and_wait(chan, "vm-nomore");
6739 } else /* Delete not valid if we haven't selected a message */
6740 cmd = 0;
6741 #ifdef IMAP_STORAGE
6742 deleted = 1;
6743 #endif
6744 break;
6746 case '8':
6747 if (vms.lastmsg > -1) {
6748 cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain);
6749 if (cmd == ERROR_LOCK_PATH) {
6750 res = cmd;
6751 goto out;
6753 } else
6754 cmd = ast_play_and_wait(chan, "vm-nomore");
6755 break;
6756 case '9':
6757 if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
6758 /* No message selected */
6759 cmd = 0;
6760 break;
6762 if (useadsi)
6763 adsi_folders(chan, 1, "Save to folder...");
6764 cmd = get_folder2(chan, "vm-savefolder", 1);
6765 box = 0; /* Shut up compiler */
6766 if (cmd == '#') {
6767 cmd = 0;
6768 break;
6769 } else if (cmd > 0) {
6770 box = cmd = cmd - '0';
6771 cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
6772 if (cmd == ERROR_LOCK_PATH) {
6773 res = cmd;
6774 goto out;
6775 #ifdef IMAP_STORAGE
6776 } else if (cmd == 10) {
6777 goto out;
6778 #endif
6779 } else if (!cmd) {
6780 vms.deleted[vms.curmsg] = 1;
6781 } else {
6782 vms.deleted[vms.curmsg] = 0;
6783 vms.heard[vms.curmsg] = 0;
6786 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
6787 if (useadsi)
6788 adsi_message(chan, &vms);
6789 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
6790 if (!cmd) {
6791 cmd = ast_play_and_wait(chan, "vm-message");
6792 if (!cmd)
6793 cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
6794 if (!cmd)
6795 cmd = ast_play_and_wait(chan, "vm-savedto");
6796 if (!cmd)
6797 cmd = vm_play_folder_name(chan, vms.fn);
6798 } else {
6799 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
6801 if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
6802 if (vms.curmsg < vms.lastmsg) {
6803 vms.curmsg++;
6804 cmd = play_message(chan, vmu, &vms);
6805 } else {
6806 cmd = ast_play_and_wait(chan, "vm-nomore");
6809 break;
6810 case '*':
6811 if (!vms.starting) {
6812 cmd = ast_play_and_wait(chan, "vm-onefor");
6813 if (!cmd)
6814 cmd = vm_play_folder_name(chan, vms.vmbox);
6815 if (!cmd)
6816 cmd = ast_play_and_wait(chan, "vm-opts");
6817 if (!cmd)
6818 cmd = vm_instructions(chan, &vms, 1);
6819 } else
6820 cmd = 0;
6821 break;
6822 case '0':
6823 cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
6824 if (useadsi)
6825 adsi_status(chan, &vms);
6826 break;
6827 default: /* Nothing */
6828 cmd = vm_instructions(chan, &vms, 0);
6829 break;
6832 if ((cmd == 't') || (cmd == '#')) {
6833 /* Timeout */
6834 res = 0;
6835 } else {
6836 /* Hangup */
6837 res = -1;
6840 out:
6841 if (res > -1) {
6842 ast_stopstream(chan);
6843 adsi_goodbye(chan);
6844 if (valid) {
6845 if (silentexit)
6846 res = ast_play_and_wait(chan, "vm-dialout");
6847 else
6848 res = ast_play_and_wait(chan, "vm-goodbye");
6849 if (res > 0)
6850 res = 0;
6852 if (useadsi)
6853 ast_adsi_unload_session(chan);
6855 if (vmu)
6856 close_mailbox(&vms, vmu);
6857 if (valid) {
6858 snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
6859 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
6860 run_externnotify(vmu->context, vmu->mailbox);
6862 #ifdef IMAP_STORAGE
6863 /* expunge message - use UID Expunge if supported on IMAP server*/
6864 if(option_debug > 2)
6865 ast_log(LOG_DEBUG, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
6866 if (vmu && deleted == 1 && expungeonhangup == 1) {
6867 #ifdef HAVE_IMAP_TK2006
6868 if (LEVELUIDPLUS (vms.mailstream)) {
6869 mail_expunge_full(vms.mailstream,NIL,EX_UID);
6870 } else
6871 #endif
6872 mail_expunge(vms.mailstream);
6874 /* before we delete the state, we should copy pertinent info
6875 * back to the persistent model */
6876 vmstate_delete(&vms);
6877 #endif
6878 if (vmu)
6879 free_user(vmu);
6880 if (vms.deleted)
6881 free(vms.deleted);
6882 if (vms.heard)
6883 free(vms.heard);
6884 ast_module_user_remove(u);
6886 return res;
6889 static int vm_exec(struct ast_channel *chan, void *data)
6891 int res = 0;
6892 struct ast_module_user *u;
6893 char *tmp;
6894 struct leave_vm_options leave_options;
6895 struct ast_flags flags = { 0 };
6896 static int deprecate_warning = 0;
6897 char *opts[OPT_ARG_ARRAY_SIZE];
6898 AST_DECLARE_APP_ARGS(args,
6899 AST_APP_ARG(argv0);
6900 AST_APP_ARG(argv1);
6903 u = ast_module_user_add(chan);
6905 memset(&leave_options, 0, sizeof(leave_options));
6907 if (chan->_state != AST_STATE_UP)
6908 ast_answer(chan);
6910 if (!ast_strlen_zero(data)) {
6911 tmp = ast_strdupa(data);
6912 AST_STANDARD_APP_ARGS(args, tmp);
6913 if (args.argc == 2) {
6914 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
6915 ast_module_user_remove(u);
6916 return -1;
6918 ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_PRIORITY_JUMP);
6919 if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
6920 int gain;
6922 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
6923 ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
6924 ast_module_user_remove(u);
6925 return -1;
6926 } else {
6927 leave_options.record_gain = (signed char) gain;
6930 } else {
6931 /* old style options parsing */
6932 int old = 0;
6933 char *orig_argv0 = args.argv0;
6934 while (*(args.argv0)) {
6935 if (*(args.argv0) == 's') {
6936 old = 1;
6937 ast_set_flag(&leave_options, OPT_SILENT);
6938 } else if (*(args.argv0) == 'b') {
6939 old = 1;
6940 ast_set_flag(&leave_options, OPT_BUSY_GREETING);
6941 } else if (*(args.argv0) == 'u') {
6942 old = 1;
6943 ast_set_flag(&leave_options, OPT_UNAVAIL_GREETING);
6944 } else if (*(args.argv0) == 'j') {
6945 old = 1;
6946 ast_set_flag(&leave_options, OPT_PRIORITY_JUMP);
6947 } else
6948 break;
6949 (args.argv0)++;
6951 if (!deprecate_warning && old) {
6952 deprecate_warning = 1;
6953 ast_log(LOG_WARNING, "Prefixing the mailbox with an option is deprecated ('%s').\n", orig_argv0);
6954 ast_log(LOG_WARNING, "Please move all leading options to the second argument.\n");
6957 } else {
6958 char tmp[256];
6959 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
6960 if (res < 0) {
6961 ast_module_user_remove(u);
6962 return res;
6964 if (ast_strlen_zero(tmp)) {
6965 ast_module_user_remove(u);
6966 return 0;
6968 args.argv0 = ast_strdupa(tmp);
6971 res = leave_voicemail(chan, args.argv0, &leave_options);
6973 if (res == ERROR_LOCK_PATH) {
6974 ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
6975 /*Send the call to n+101 priority, where n is the current priority*/
6976 if (ast_test_flag(&leave_options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
6977 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
6978 ast_log(LOG_WARNING, "Extension %s, priority %d doesn't exist.\n", chan->exten, chan->priority + 101);
6979 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
6980 res = 0;
6983 ast_module_user_remove(u);
6985 return res;
6988 static struct ast_vm_user *find_or_create(char *context, char *mbox)
6990 struct ast_vm_user *vmu;
6991 AST_LIST_TRAVERSE(&users, vmu, list) {
6992 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mbox, vmu->mailbox))
6993 break;
6994 if (context && (!strcasecmp(context, vmu->context)) && (!strcasecmp(mbox, vmu->mailbox)))
6995 break;
6998 if (!vmu) {
6999 if ((vmu = ast_calloc(1, sizeof(*vmu)))) {
7000 ast_copy_string(vmu->context, context, sizeof(vmu->context));
7001 ast_copy_string(vmu->mailbox, mbox, sizeof(vmu->mailbox));
7002 AST_LIST_INSERT_TAIL(&users, vmu, list);
7005 return vmu;
7008 static int append_mailbox(char *context, char *mbox, char *data)
7010 /* Assumes lock is already held */
7011 char *tmp;
7012 char *stringp;
7013 char *s;
7014 struct ast_vm_user *vmu;
7016 tmp = ast_strdupa(data);
7018 if ((vmu = find_or_create(context, mbox))) {
7019 populate_defaults(vmu);
7021 stringp = tmp;
7022 if ((s = strsep(&stringp, ",")))
7023 ast_copy_string(vmu->password, s, sizeof(vmu->password));
7024 if (stringp && (s = strsep(&stringp, ",")))
7025 ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
7026 if (stringp && (s = strsep(&stringp, ",")))
7027 ast_copy_string(vmu->email, s, sizeof(vmu->email));
7028 if (stringp && (s = strsep(&stringp, ",")))
7029 ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
7030 if (stringp && (s = strsep(&stringp, ",")))
7031 apply_options(vmu, s);
7033 return 0;
7036 static int vm_box_exists(struct ast_channel *chan, void *data)
7038 struct ast_module_user *u;
7039 struct ast_vm_user svm;
7040 char *context, *box;
7041 int priority_jump = 0;
7042 AST_DECLARE_APP_ARGS(args,
7043 AST_APP_ARG(mbox);
7044 AST_APP_ARG(options);
7047 if (ast_strlen_zero(data)) {
7048 ast_log(LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
7049 return -1;
7052 u = ast_module_user_add(chan);
7054 box = ast_strdupa(data);
7056 AST_STANDARD_APP_ARGS(args, box);
7058 if (args.options) {
7059 if (strchr(args.options, 'j'))
7060 priority_jump = 1;
7063 if ((context = strchr(args.mbox, '@'))) {
7064 *context = '\0';
7065 context++;
7068 if (find_user(&svm, context, args.mbox)) {
7069 pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
7070 if (priority_jump || ast_opt_priority_jumping)
7071 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
7072 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);
7073 } else
7074 pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
7075 ast_module_user_remove(u);
7076 return 0;
7079 static int vmauthenticate(struct ast_channel *chan, void *data)
7081 struct ast_module_user *u;
7082 char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
7083 struct ast_vm_user vmus;
7084 char *options = NULL;
7085 int silent = 0, skipuser = 0;
7086 int res = -1;
7088 u = ast_module_user_add(chan);
7090 if (s) {
7091 s = ast_strdupa(s);
7092 user = strsep(&s, "|");
7093 options = strsep(&s, "|");
7094 if (user) {
7095 s = user;
7096 user = strsep(&s, "@");
7097 context = strsep(&s, "");
7098 if (!ast_strlen_zero(user))
7099 skipuser++;
7100 ast_copy_string(mailbox, user, sizeof(mailbox));
7104 if (options) {
7105 silent = (strchr(options, 's')) != NULL;
7108 if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
7109 pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
7110 pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
7111 ast_play_and_wait(chan, "auth-thankyou");
7112 res = 0;
7115 ast_module_user_remove(u);
7116 return res;
7119 static char voicemail_show_users_help[] =
7120 "Usage: voicemail show users [for <context>]\n"
7121 " Lists all mailboxes currently set up\n";
7123 static char voicemail_show_zones_help[] =
7124 "Usage: voicemail show zones\n"
7125 " Lists zone message formats\n";
7127 static int handle_voicemail_show_users(int fd, int argc, char *argv[])
7129 struct ast_vm_user *vmu;
7130 char *output_format = "%-10s %-5s %-25s %-10s %6s\n";
7132 if ((argc < 3) || (argc > 5) || (argc == 4)) return RESULT_SHOWUSAGE;
7133 else if ((argc == 5) && strcmp(argv[3],"for")) return RESULT_SHOWUSAGE;
7135 AST_LIST_LOCK(&users);
7136 if (!AST_LIST_EMPTY(&users)) {
7137 if (argc == 3)
7138 ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
7139 else {
7140 int count = 0;
7141 AST_LIST_TRAVERSE(&users, vmu, list) {
7142 if (!strcmp(argv[4],vmu->context))
7143 count++;
7145 if (count) {
7146 ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
7147 } else {
7148 ast_cli(fd, "No such voicemail context \"%s\"\n", argv[4]);
7149 AST_LIST_UNLOCK(&users);
7150 return RESULT_FAILURE;
7153 AST_LIST_TRAVERSE(&users, vmu, list) {
7154 int newmsgs = 0, oldmsgs = 0;
7155 char count[12], tmp[256] = "";
7157 if ((argc == 3) || ((argc == 5) && !strcmp(argv[4],vmu->context))) {
7158 snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
7159 inboxcount(tmp, &newmsgs, &oldmsgs);
7160 snprintf(count,sizeof(count),"%d",newmsgs);
7161 ast_cli(fd, output_format, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
7164 } else {
7165 ast_cli(fd, "There are no voicemail users currently defined\n");
7166 AST_LIST_UNLOCK(&users);
7167 return RESULT_FAILURE;
7169 AST_LIST_UNLOCK(&users);
7170 return RESULT_SUCCESS;
7173 static int handle_voicemail_show_zones(int fd, int argc, char *argv[])
7175 struct vm_zone *zone;
7176 char *output_format = "%-15s %-20s %-45s\n";
7177 int res = RESULT_SUCCESS;
7179 if (argc != 3)
7180 return RESULT_SHOWUSAGE;
7182 AST_LIST_LOCK(&zones);
7183 if (!AST_LIST_EMPTY(&zones)) {
7184 ast_cli(fd, output_format, "Zone", "Timezone", "Message Format");
7185 AST_LIST_TRAVERSE(&zones, zone, list) {
7186 ast_cli(fd, output_format, zone->name, zone->timezone, zone->msg_format);
7188 } else {
7189 ast_cli(fd, "There are no voicemail zones currently defined\n");
7190 res = RESULT_FAILURE;
7192 AST_LIST_UNLOCK(&zones);
7194 return res;
7197 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
7199 int which = 0;
7200 int wordlen;
7201 struct ast_vm_user *vmu;
7202 const char *context = "";
7204 /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
7205 if (pos > 4)
7206 return NULL;
7207 if (pos == 3)
7208 return (state == 0) ? ast_strdup("for") : NULL;
7209 wordlen = strlen(word);
7210 AST_LIST_TRAVERSE(&users, vmu, list) {
7211 if (!strncasecmp(word, vmu->context, wordlen)) {
7212 if (context && strcmp(context, vmu->context) && ++which > state)
7213 return ast_strdup(vmu->context);
7214 /* ignore repeated contexts ? */
7215 context = vmu->context;
7218 return NULL;
7221 static struct ast_cli_entry cli_show_voicemail_users_deprecated = {
7222 { "show", "voicemail", "users", NULL },
7223 handle_voicemail_show_users, NULL,
7224 NULL, complete_voicemail_show_users };
7226 static struct ast_cli_entry cli_show_voicemail_zones_deprecated = {
7227 { "show", "voicemail", "zones", NULL },
7228 handle_voicemail_show_zones, NULL,
7229 NULL, NULL };
7231 static struct ast_cli_entry cli_voicemail[] = {
7232 { { "voicemail", "show", "users", NULL },
7233 handle_voicemail_show_users, "List defined voicemail boxes",
7234 voicemail_show_users_help, complete_voicemail_show_users, &cli_show_voicemail_users_deprecated },
7236 { { "voicemail", "show", "zones", NULL },
7237 handle_voicemail_show_zones, "List zone message formats",
7238 voicemail_show_zones_help, NULL, &cli_show_voicemail_zones_deprecated },
7241 static int load_config(void)
7243 struct ast_vm_user *cur;
7244 struct vm_zone *zcur;
7245 struct ast_config *cfg, *ucfg;
7246 char *cat;
7247 struct ast_variable *var;
7248 const char *notifystr = NULL;
7249 const char *smdistr = NULL;
7250 const char *astattach;
7251 const char *astsearch;
7252 const char *astsaycid;
7253 const char *send_voicemail;
7254 #ifdef IMAP_STORAGE
7255 const char *imap_server;
7256 const char *imap_port;
7257 const char *imap_flags;
7258 const char *imap_folder;
7259 const char *auth_user;
7260 const char *auth_password;
7261 const char *expunge_on_hangup;
7262 #endif
7263 const char *astcallop;
7264 const char *astreview;
7265 const char *asttempgreetwarn;
7266 const char *astskipcmd;
7267 const char *asthearenv;
7268 const char *astsaydurationinfo;
7269 const char *astsaydurationminfo;
7270 const char *silencestr;
7271 const char *maxmsgstr;
7272 const char *astdirfwd;
7273 const char *thresholdstr;
7274 const char *fmt;
7275 const char *astemail;
7276 const char *ucontext;
7277 const char *astmailcmd = SENDMAIL;
7278 const char *astforcename;
7279 const char *astforcegreet;
7280 const char *s;
7281 char *q,*stringp;
7282 const char *dialoutcxt = NULL;
7283 const char *callbackcxt = NULL;
7284 const char *exitcxt = NULL;
7285 const char *extpc;
7286 const char *emaildateformatstr;
7287 const char *volgainstr;
7288 int x;
7289 int tmpadsi[4];
7291 cfg = ast_config_load(VOICEMAIL_CONFIG);
7293 AST_LIST_LOCK(&users);
7294 while ((cur = AST_LIST_REMOVE_HEAD(&users, list))) {
7295 ast_set_flag(cur, VM_ALLOCED);
7296 free_user(cur);
7299 AST_LIST_LOCK(&zones);
7300 while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
7301 free_zone(zcur);
7302 AST_LIST_UNLOCK(&zones);
7304 memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
7306 if (cfg) {
7307 /* General settings */
7309 if (!(ucontext = ast_variable_retrieve(cfg, "general", "userscontext")))
7310 ucontext = "default";
7311 ast_copy_string(userscontext, ucontext, sizeof(userscontext));
7312 /* Attach voice message to mail message ? */
7313 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach")))
7314 astattach = "yes";
7315 ast_set2_flag((&globalflags), ast_true(astattach), VM_ATTACH);
7317 if (!(astsearch = ast_variable_retrieve(cfg, "general", "searchcontexts")))
7318 astsearch = "no";
7319 ast_set2_flag((&globalflags), ast_true(astsearch), VM_SEARCH);
7321 volgain = 0.0;
7322 if ((volgainstr = ast_variable_retrieve(cfg, "general", "volgain")))
7323 sscanf(volgainstr, "%lf", &volgain);
7325 #ifdef ODBC_STORAGE
7326 strcpy(odbc_database, "asterisk");
7327 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
7328 ast_copy_string(odbc_database, thresholdstr, sizeof(odbc_database));
7330 strcpy(odbc_table, "voicemessages");
7331 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbctable"))) {
7332 ast_copy_string(odbc_table, thresholdstr, sizeof(odbc_table));
7334 #endif
7335 /* Mail command */
7336 strcpy(mailcmd, SENDMAIL);
7337 if ((astmailcmd = ast_variable_retrieve(cfg, "general", "mailcmd")))
7338 ast_copy_string(mailcmd, astmailcmd, sizeof(mailcmd)); /* User setting */
7340 maxsilence = 0;
7341 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
7342 maxsilence = atoi(silencestr);
7343 if (maxsilence > 0)
7344 maxsilence *= 1000;
7347 if (!(maxmsgstr = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
7348 maxmsg = MAXMSG;
7349 } else {
7350 maxmsg = atoi(maxmsgstr);
7351 if (maxmsg <= 0) {
7352 ast_log(LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", maxmsgstr, MAXMSG);
7353 maxmsg = MAXMSG;
7354 } else if (maxmsg > MAXMSGLIMIT) {
7355 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, maxmsgstr);
7356 maxmsg = MAXMSGLIMIT;
7360 /* Load date format config for voicemail mail */
7361 if ((emaildateformatstr = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
7362 ast_copy_string(emaildateformat, emaildateformatstr, sizeof(emaildateformat));
7365 /* External password changing command */
7366 if ((extpc = ast_variable_retrieve(cfg, "general", "externpass"))) {
7367 ast_copy_string(ext_pass_cmd,extpc,sizeof(ext_pass_cmd));
7369 #ifdef IMAP_STORAGE
7370 /* IMAP server address */
7371 if ((imap_server = ast_variable_retrieve(cfg, "general", "imapserver"))) {
7372 ast_copy_string(imapserver, imap_server, sizeof(imapserver));
7373 } else {
7374 ast_copy_string(imapserver,"localhost", sizeof(imapserver));
7376 /* IMAP server port */
7377 if ((imap_port = ast_variable_retrieve(cfg, "general", "imapport"))) {
7378 ast_copy_string(imapport, imap_port, sizeof(imapport));
7379 } else {
7380 ast_copy_string(imapport,"143", sizeof(imapport));
7382 /* IMAP server flags */
7383 if ((imap_flags = ast_variable_retrieve(cfg, "general", "imapflags"))) {
7384 ast_copy_string(imapflags, imap_flags, sizeof(imapflags));
7386 /* IMAP server master username */
7387 if ((auth_user = ast_variable_retrieve(cfg, "general", "authuser"))) {
7388 ast_copy_string(authuser, auth_user, sizeof(authuser));
7390 /* IMAP server master password */
7391 if ((auth_password = ast_variable_retrieve(cfg, "general", "authpassword"))) {
7392 ast_copy_string(authpassword, auth_password, sizeof(authpassword));
7394 /* Expunge on exit */
7395 if ((expunge_on_hangup = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
7396 if(ast_false(expunge_on_hangup))
7397 expungeonhangup = 0;
7398 else
7399 expungeonhangup = 1;
7400 } else {
7401 expungeonhangup = 1;
7403 /* IMAP voicemail folder */
7404 if ((imap_folder = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
7405 ast_copy_string(imapfolder, imap_folder, sizeof(imapfolder));
7406 } else {
7407 ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
7409 #endif
7410 /* External voicemail notify application */
7412 if ((notifystr = ast_variable_retrieve(cfg, "general", "externnotify"))) {
7413 ast_copy_string(externnotify, notifystr, sizeof(externnotify));
7414 if (option_debug > 2)
7415 ast_log(LOG_DEBUG, "found externnotify: %s\n", externnotify);
7416 if (!strcasecmp(externnotify, "smdi")) {
7417 if (option_debug)
7418 ast_log(LOG_DEBUG, "Using SMDI for external voicemail notification\n");
7419 if ((smdistr = ast_variable_retrieve(cfg, "general", "smdiport"))) {
7420 smdi_iface = ast_smdi_interface_find(smdistr);
7421 } else {
7422 if (option_debug)
7423 ast_log(LOG_DEBUG, "No SMDI interface set, trying default (/dev/ttyS0)\n");
7424 smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
7427 if (!smdi_iface) {
7428 ast_log(LOG_ERROR, "No valid SMDI interface specfied, disabling external voicemail notification\n");
7429 externnotify[0] = '\0';
7430 } else {
7431 if (option_debug > 2)
7432 ast_log(LOG_DEBUG, "Using SMDI port %s\n", smdi_iface->name);
7435 } else {
7436 externnotify[0] = '\0';
7439 /* Silence treshold */
7440 silencethreshold = 256;
7441 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
7442 silencethreshold = atoi(thresholdstr);
7444 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
7445 astemail = ASTERISK_USERNAME;
7446 ast_copy_string(serveremail, astemail, sizeof(serveremail));
7448 vmmaxmessage = 0;
7449 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
7450 if (sscanf(s, "%d", &x) == 1) {
7451 vmmaxmessage = x;
7452 } else {
7453 ast_log(LOG_WARNING, "Invalid max message time length\n");
7457 vmminmessage = 0;
7458 if ((s = ast_variable_retrieve(cfg, "general", "minmessage"))) {
7459 if (sscanf(s, "%d", &x) == 1) {
7460 vmminmessage = x;
7461 if (maxsilence <= vmminmessage)
7462 ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
7463 } else {
7464 ast_log(LOG_WARNING, "Invalid min message time length\n");
7467 fmt = ast_variable_retrieve(cfg, "general", "format");
7468 if (!fmt)
7469 fmt = "wav";
7470 ast_copy_string(vmfmts, fmt, sizeof(vmfmts));
7472 skipms = 3000;
7473 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
7474 if (sscanf(s, "%d", &x) == 1) {
7475 maxgreet = x;
7476 } else {
7477 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
7481 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
7482 if (sscanf(s, "%d", &x) == 1) {
7483 skipms = x;
7484 } else {
7485 ast_log(LOG_WARNING, "Invalid skipms value\n");
7489 maxlogins = 3;
7490 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
7491 if (sscanf(s, "%d", &x) == 1) {
7492 maxlogins = x;
7493 } else {
7494 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
7498 /* Force new user to record name ? */
7499 if (!(astforcename = ast_variable_retrieve(cfg, "general", "forcename")))
7500 astforcename = "no";
7501 ast_set2_flag((&globalflags), ast_true(astforcename), VM_FORCENAME);
7503 /* Force new user to record greetings ? */
7504 if (!(astforcegreet = ast_variable_retrieve(cfg, "general", "forcegreetings")))
7505 astforcegreet = "no";
7506 ast_set2_flag((&globalflags), ast_true(astforcegreet), VM_FORCEGREET);
7508 if ((s = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))){
7509 if (option_debug > 2)
7510 ast_log(LOG_DEBUG,"VM_CID Internal context string: %s\n",s);
7511 stringp = ast_strdupa(s);
7512 for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
7513 if (!ast_strlen_zero(stringp)) {
7514 q = strsep(&stringp,",");
7515 while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
7516 q++;
7517 ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
7518 if (option_debug > 2)
7519 ast_log(LOG_DEBUG,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
7520 } else {
7521 cidinternalcontexts[x][0] = '\0';
7525 if (!(astreview = ast_variable_retrieve(cfg, "general", "review"))){
7526 if (option_debug)
7527 ast_log(LOG_DEBUG,"VM Review Option disabled globally\n");
7528 astreview = "no";
7530 ast_set2_flag((&globalflags), ast_true(astreview), VM_REVIEW);
7532 /*Temperary greeting reminder */
7533 if (!(asttempgreetwarn = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
7534 if (option_debug)
7535 ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option disabled globally\n");
7536 asttempgreetwarn = "no";
7537 } else {
7538 if (option_debug)
7539 ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option enabled globally\n");
7541 ast_set2_flag((&globalflags), ast_true(asttempgreetwarn), VM_TEMPGREETWARN);
7543 if (!(astcallop = ast_variable_retrieve(cfg, "general", "operator"))){
7544 if (option_debug)
7545 ast_log(LOG_DEBUG,"VM Operator break disabled globally\n");
7546 astcallop = "no";
7548 ast_set2_flag((&globalflags), ast_true(astcallop), VM_OPERATOR);
7550 if (!(astsaycid = ast_variable_retrieve(cfg, "general", "saycid"))) {
7551 if (option_debug)
7552 ast_log(LOG_DEBUG,"VM CID Info before msg disabled globally\n");
7553 astsaycid = "no";
7555 ast_set2_flag((&globalflags), ast_true(astsaycid), VM_SAYCID);
7557 if (!(send_voicemail = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
7558 if (option_debug)
7559 ast_log(LOG_DEBUG,"Send Voicemail msg disabled globally\n");
7560 send_voicemail = "no";
7562 ast_set2_flag((&globalflags), ast_true(send_voicemail), VM_SVMAIL);
7564 if (!(asthearenv = ast_variable_retrieve(cfg, "general", "envelope"))) {
7565 if (option_debug)
7566 ast_log(LOG_DEBUG,"ENVELOPE before msg enabled globally\n");
7567 asthearenv = "yes";
7569 ast_set2_flag((&globalflags), ast_true(asthearenv), VM_ENVELOPE);
7571 if (!(astsaydurationinfo = ast_variable_retrieve(cfg, "general", "sayduration"))) {
7572 if (option_debug)
7573 ast_log(LOG_DEBUG,"Duration info before msg enabled globally\n");
7574 astsaydurationinfo = "yes";
7576 ast_set2_flag((&globalflags), ast_true(astsaydurationinfo), VM_SAYDURATION);
7578 saydurationminfo = 2;
7579 if ((astsaydurationminfo = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
7580 if (sscanf(astsaydurationminfo, "%d", &x) == 1) {
7581 saydurationminfo = x;
7582 } else {
7583 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
7587 if (!(astskipcmd = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
7588 if (option_debug)
7589 ast_log(LOG_DEBUG,"We are not going to skip to the next msg after save/delete\n");
7590 astskipcmd = "no";
7592 ast_set2_flag((&globalflags), ast_true(astskipcmd), VM_SKIPAFTERCMD);
7594 if ((dialoutcxt = ast_variable_retrieve(cfg, "general", "dialout"))) {
7595 ast_copy_string(dialcontext, dialoutcxt, sizeof(dialcontext));
7596 if (option_debug)
7597 ast_log(LOG_DEBUG, "found dialout context: %s\n", dialcontext);
7598 } else {
7599 dialcontext[0] = '\0';
7602 if ((callbackcxt = ast_variable_retrieve(cfg, "general", "callback"))) {
7603 ast_copy_string(callcontext, callbackcxt, sizeof(callcontext));
7604 if (option_debug)
7605 ast_log(LOG_DEBUG, "found callback context: %s\n", callcontext);
7606 } else {
7607 callcontext[0] = '\0';
7610 if ((exitcxt = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
7611 ast_copy_string(exitcontext, exitcxt, sizeof(exitcontext));
7612 if (option_debug)
7613 ast_log(LOG_DEBUG, "found operator context: %s\n", exitcontext);
7614 } else {
7615 exitcontext[0] = '\0';
7618 if (!(astdirfwd = ast_variable_retrieve(cfg, "general", "usedirectory")))
7619 astdirfwd = "no";
7620 ast_set2_flag((&globalflags), ast_true(astdirfwd), VM_DIRECFORWARD);
7621 if ((ucfg = ast_config_load("users.conf"))) {
7622 for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
7623 if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
7624 continue;
7625 if ((cur = find_or_create(userscontext, cat))) {
7626 populate_defaults(cur);
7627 apply_options_full(cur, ast_variable_browse(ucfg, cat));
7628 ast_copy_string(cur->context, userscontext, sizeof(cur->context));
7631 ast_config_destroy(ucfg);
7633 cat = ast_category_browse(cfg, NULL);
7634 while (cat) {
7635 if (strcasecmp(cat, "general")) {
7636 var = ast_variable_browse(cfg, cat);
7637 if (strcasecmp(cat, "zonemessages")) {
7638 /* Process mailboxes in this context */
7639 while (var) {
7640 append_mailbox(cat, var->name, var->value);
7641 var = var->next;
7643 } else {
7644 /* Timezones in this context */
7645 while (var) {
7646 struct vm_zone *z;
7647 if ((z = ast_malloc(sizeof(*z)))) {
7648 char *msg_format, *timezone;
7649 msg_format = ast_strdupa(var->value);
7650 timezone = strsep(&msg_format, "|");
7651 if (msg_format) {
7652 ast_copy_string(z->name, var->name, sizeof(z->name));
7653 ast_copy_string(z->timezone, timezone, sizeof(z->timezone));
7654 ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
7655 AST_LIST_LOCK(&zones);
7656 AST_LIST_INSERT_HEAD(&zones, z, list);
7657 AST_LIST_UNLOCK(&zones);
7658 } else {
7659 ast_log(LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
7660 free(z);
7662 } else {
7663 free(z);
7664 AST_LIST_UNLOCK(&users);
7665 ast_config_destroy(cfg);
7666 return -1;
7668 var = var->next;
7672 cat = ast_category_browse(cfg, cat);
7674 memset(fromstring,0,sizeof(fromstring));
7675 memset(pagerfromstring,0,sizeof(pagerfromstring));
7676 memset(emailtitle,0,sizeof(emailtitle));
7677 strcpy(charset, "ISO-8859-1");
7678 if (emailbody) {
7679 free(emailbody);
7680 emailbody = NULL;
7682 if (emailsubject) {
7683 free(emailsubject);
7684 emailsubject = NULL;
7686 if (pagerbody) {
7687 free(pagerbody);
7688 pagerbody = NULL;
7690 if (pagersubject) {
7691 free(pagersubject);
7692 pagersubject = NULL;
7694 if ((s = ast_variable_retrieve(cfg, "general", "pbxskip")))
7695 ast_set2_flag((&globalflags), ast_true(s), VM_PBXSKIP);
7696 if ((s = ast_variable_retrieve(cfg, "general", "fromstring")))
7697 ast_copy_string(fromstring,s,sizeof(fromstring));
7698 if ((s = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
7699 ast_copy_string(pagerfromstring,s,sizeof(pagerfromstring));
7700 if ((s = ast_variable_retrieve(cfg, "general", "charset")))
7701 ast_copy_string(charset,s,sizeof(charset));
7702 if ((s = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
7703 sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
7704 for (x = 0; x < 4; x++) {
7705 memcpy(&adsifdn[x], &tmpadsi[x], 1);
7708 if ((s = ast_variable_retrieve(cfg, "general", "adsisec"))) {
7709 sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
7710 for (x = 0; x < 4; x++) {
7711 memcpy(&adsisec[x], &tmpadsi[x], 1);
7714 if ((s = ast_variable_retrieve(cfg, "general", "adsiver")))
7715 if (atoi(s)) {
7716 adsiver = atoi(s);
7718 if ((s = ast_variable_retrieve(cfg, "general", "emailtitle"))) {
7719 ast_log(LOG_NOTICE, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
7720 ast_copy_string(emailtitle,s,sizeof(emailtitle));
7722 if ((s = ast_variable_retrieve(cfg, "general", "emailsubject")))
7723 emailsubject = ast_strdup(s);
7724 if ((s = ast_variable_retrieve(cfg, "general", "emailbody"))) {
7725 char *tmpread, *tmpwrite;
7726 emailbody = ast_strdup(s);
7728 /* substitute strings \t and \n into the appropriate characters */
7729 tmpread = tmpwrite = emailbody;
7730 while ((tmpwrite = strchr(tmpread,'\\'))) {
7731 switch (tmpwrite[1]) {
7732 case 'r':
7733 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7734 *tmpwrite = '\r';
7735 break;
7736 case 'n':
7737 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7738 *tmpwrite = '\n';
7739 break;
7740 case 't':
7741 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7742 *tmpwrite = '\t';
7743 break;
7744 default:
7745 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
7747 tmpread = tmpwrite + 1;
7750 if ((s = ast_variable_retrieve(cfg, "general", "pagersubject")))
7751 pagersubject = ast_strdup(s);
7752 if ((s = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
7753 char *tmpread, *tmpwrite;
7754 pagerbody = ast_strdup(s);
7756 /* substitute strings \t and \n into the appropriate characters */
7757 tmpread = tmpwrite = pagerbody;
7758 while ((tmpwrite = strchr(tmpread, '\\'))) {
7759 switch (tmpwrite[1]) {
7760 case 'r':
7761 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7762 *tmpwrite = '\r';
7763 break;
7764 case 'n':
7765 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7766 *tmpwrite = '\n';
7767 break;
7768 case 't':
7769 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7770 *tmpwrite = '\t';
7771 break;
7772 default:
7773 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
7775 tmpread = tmpwrite + 1;
7778 AST_LIST_UNLOCK(&users);
7779 ast_config_destroy(cfg);
7780 return 0;
7781 } else {
7782 AST_LIST_UNLOCK(&users);
7783 ast_log(LOG_WARNING, "Failed to load configuration file.\n");
7784 return 0;
7788 static int reload(void)
7790 return(load_config());
7793 static int unload_module(void)
7795 int res;
7797 res = ast_unregister_application(app);
7798 res |= ast_unregister_application(app2);
7799 res |= ast_unregister_application(app3);
7800 res |= ast_unregister_application(app4);
7801 ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
7802 ast_uninstall_vm_functions();
7804 ast_module_user_hangup_all();
7806 return res;
7809 static int load_module(void)
7811 int res;
7812 my_umask = umask(0);
7813 umask(my_umask);
7814 res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
7815 res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
7816 res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
7817 res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
7818 if (res)
7819 return(res);
7821 if ((res=load_config())) {
7822 return(res);
7825 ast_cli_register_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
7827 /* compute the location of the voicemail spool directory */
7828 snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
7830 ast_install_vm_functions(has_voicemail, inboxcount, messagecount);
7832 return res;
7835 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
7837 int cmd = 0;
7838 char destination[80] = "";
7839 int retries = 0;
7841 if (!num) {
7842 if (option_verbose > 2)
7843 ast_verbose( VERBOSE_PREFIX_3 "Destination number will be entered manually\n");
7844 while (retries < 3 && cmd != 't') {
7845 destination[1] = '\0';
7846 destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
7847 if (!cmd)
7848 destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
7849 if (!cmd)
7850 destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
7851 if (!cmd) {
7852 cmd = ast_waitfordigit(chan, 6000);
7853 if (cmd)
7854 destination[0] = cmd;
7856 if (!cmd) {
7857 retries++;
7858 } else {
7860 if (cmd < 0)
7861 return 0;
7862 if (cmd == '*') {
7863 if (option_verbose > 2)
7864 ast_verbose( VERBOSE_PREFIX_3 "User hit '*' to cancel outgoing call\n");
7865 return 0;
7867 if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0)
7868 retries++;
7869 else
7870 cmd = 't';
7873 if (retries >= 3) {
7874 return 0;
7877 } else {
7878 if (option_verbose > 2)
7879 ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
7880 ast_copy_string(destination, num, sizeof(destination));
7883 if (!ast_strlen_zero(destination)) {
7884 if (destination[strlen(destination) -1 ] == '*')
7885 return 0;
7886 if (option_verbose > 2)
7887 ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
7888 ast_copy_string(chan->exten, destination, sizeof(chan->exten));
7889 ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
7890 chan->priority = 0;
7891 return 9;
7893 return 0;
7896 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)
7898 int res = 0;
7899 #ifdef IMAP_STORAGE
7900 char origtimeS[256],cidS[256],contextS[256];
7901 char *header_content,*temp;
7902 #endif
7903 char filename[PATH_MAX];
7904 struct ast_config *msg_cfg = NULL;
7905 const char *origtime, *context;
7906 char *cid, *name, *num;
7907 int retries = 0;
7909 vms->starting = 0;
7910 #ifdef IMAP_STORAGE
7911 /* START HERE */
7912 /* get the message info!! */
7913 if(option_debug > 2)
7914 ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
7915 if (vms->msgArray[vms->curmsg] == 0) {
7916 ast_log (LOG_WARNING,"Trying to access unknown message\n");
7917 return -1;
7920 /* This will only work for new messages... */
7921 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
7922 /* empty string means no valid header */
7923 if (ast_strlen_zero(header_content)) {
7924 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
7925 return -1;
7928 /* Get info from headers!! */
7929 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:");
7931 if (temp)
7932 ast_copy_string(cidS,temp, sizeof(cidS));
7933 else
7934 cidS[0] = '\0';
7936 cid = &cidS[0];
7937 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Context:");
7939 if (temp)
7940 ast_copy_string(contextS,temp, sizeof(contextS));
7941 else
7942 contextS[0] = '\0';
7944 context = &contextS[0];
7945 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:");
7947 if (temp)
7948 ast_copy_string(origtimeS,temp, sizeof(origtimeS));
7949 else
7950 origtimeS[0] = '\0';
7952 origtime = &origtimeS[0];
7954 ast_copy_string(filename, "IMAP_STORAGE", sizeof(filename));
7955 #else
7956 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
7958 /* Retrieve info from VM attribute file */
7960 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
7961 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
7962 RETRIEVE(vms->curdir, vms->curmsg);
7963 msg_cfg = ast_config_load(filename);
7964 DISPOSE(vms->curdir, vms->curmsg);
7965 if (!msg_cfg) {
7966 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
7967 return 0;
7970 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
7971 ast_config_destroy(msg_cfg);
7972 return 0;
7975 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
7977 context = ast_variable_retrieve(msg_cfg, "message", "context");
7978 if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
7979 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
7980 #endif
7981 switch (option) {
7982 case 3:
7983 if (!res)
7984 res = play_message_datetime(chan, vmu, origtime, filename);
7985 if (!res)
7986 res = play_message_callerid(chan, vms, cid, context, 0);
7988 res = 't';
7989 break;
7991 case 2: /* Call back */
7993 if (ast_strlen_zero(cid))
7994 break;
7996 ast_callerid_parse(cid, &name, &num);
7997 while ((res > -1) && (res != 't')) {
7998 switch (res) {
7999 case '1':
8000 if (num) {
8001 /* Dial the CID number */
8002 res = dialout(chan, vmu, num, vmu->callback);
8003 if (res) {
8004 ast_config_destroy(msg_cfg);
8005 return 9;
8007 } else {
8008 res = '2';
8010 break;
8012 case '2':
8013 /* Want to enter a different number, can only do this if there's a dialout context for this user */
8014 if (!ast_strlen_zero(vmu->dialout)) {
8015 res = dialout(chan, vmu, NULL, vmu->dialout);
8016 if (res) {
8017 ast_config_destroy(msg_cfg);
8018 return 9;
8020 } else {
8021 if (option_verbose > 2)
8022 ast_verbose( VERBOSE_PREFIX_3 "Caller can not specify callback number - no dialout context available\n");
8023 res = ast_play_and_wait(chan, "vm-sorry");
8025 ast_config_destroy(msg_cfg);
8026 return res;
8027 case '*':
8028 res = 't';
8029 break;
8030 case '3':
8031 case '4':
8032 case '5':
8033 case '6':
8034 case '7':
8035 case '8':
8036 case '9':
8037 case '0':
8039 res = ast_play_and_wait(chan, "vm-sorry");
8040 retries++;
8041 break;
8042 default:
8043 if (num) {
8044 if (option_verbose > 2)
8045 ast_verbose( VERBOSE_PREFIX_3 "Confirm CID number '%s' is number to use for callback\n", num);
8046 res = ast_play_and_wait(chan, "vm-num-i-have");
8047 if (!res)
8048 res = play_message_callerid(chan, vms, num, vmu->context, 1);
8049 if (!res)
8050 res = ast_play_and_wait(chan, "vm-tocallnum");
8051 /* Only prompt for a caller-specified number if there is a dialout context specified */
8052 if (!ast_strlen_zero(vmu->dialout)) {
8053 if (!res)
8054 res = ast_play_and_wait(chan, "vm-calldiffnum");
8056 } else {
8057 res = ast_play_and_wait(chan, "vm-nonumber");
8058 if (!ast_strlen_zero(vmu->dialout)) {
8059 if (!res)
8060 res = ast_play_and_wait(chan, "vm-toenternumber");
8063 if (!res)
8064 res = ast_play_and_wait(chan, "vm-star-cancel");
8065 if (!res)
8066 res = ast_waitfordigit(chan, 6000);
8067 if (!res) {
8068 retries++;
8069 if (retries > 3)
8070 res = 't';
8072 break;
8075 if (res == 't')
8076 res = 0;
8077 else if (res == '*')
8078 res = -1;
8080 break;
8082 case 1: /* Reply */
8083 /* Send reply directly to sender */
8084 if (ast_strlen_zero(cid))
8085 break;
8087 ast_callerid_parse(cid, &name, &num);
8088 if (!num) {
8089 if (option_verbose > 2)
8090 ast_verbose(VERBOSE_PREFIX_3 "No CID number available, no reply sent\n");
8091 if (!res)
8092 res = ast_play_and_wait(chan, "vm-nonumber");
8093 ast_config_destroy(msg_cfg);
8094 return res;
8095 } else {
8096 struct ast_vm_user vmu2;
8097 if (find_user(&vmu2, vmu->context, num)) {
8098 struct leave_vm_options leave_options;
8099 char mailbox[AST_MAX_EXTENSION * 2 + 2];
8100 snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
8102 if (option_verbose > 2)
8103 ast_verbose(VERBOSE_PREFIX_3 "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
8105 memset(&leave_options, 0, sizeof(leave_options));
8106 leave_options.record_gain = record_gain;
8107 res = leave_voicemail(chan, mailbox, &leave_options);
8108 if (!res)
8109 res = 't';
8110 ast_config_destroy(msg_cfg);
8111 return res;
8112 } else {
8113 /* Sender has no mailbox, can't reply */
8114 if (option_verbose > 2)
8115 ast_verbose( VERBOSE_PREFIX_3 "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
8116 ast_play_and_wait(chan, "vm-nobox");
8117 res = 't';
8118 ast_config_destroy(msg_cfg);
8119 return res;
8122 res = 0;
8124 break;
8127 #ifndef IMAP_STORAGE
8128 ast_config_destroy(msg_cfg);
8130 if (!res) {
8131 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
8132 vms->heard[msg] = 1;
8133 res = wait_file(chan, vms, vms->fn);
8135 #endif
8136 return res;
8139 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
8140 int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
8141 signed char record_gain, struct vm_state *vms)
8143 /* Record message & let caller review or re-record it, or set options if applicable */
8144 int res = 0;
8145 int cmd = 0;
8146 int max_attempts = 3;
8147 int attempts = 0;
8148 int recorded = 0;
8149 int message_exists = 0;
8150 signed char zero_gain = 0;
8151 char tempfile[PATH_MAX];
8152 char *acceptdtmf = "#";
8153 char *canceldtmf = "";
8155 /* Note that urgent and private are for flagging messages as such in the future */
8157 /* barf if no pointer passed to store duration in */
8158 if (duration == NULL) {
8159 ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
8160 return -1;
8163 if (!outsidecaller)
8164 snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
8165 else
8166 ast_copy_string(tempfile, recordfile, sizeof(tempfile));
8168 cmd = '3'; /* Want to start by recording */
8170 while ((cmd >= 0) && (cmd != 't')) {
8171 switch (cmd) {
8172 case '1':
8173 if (!message_exists) {
8174 /* In this case, 1 is to record a message */
8175 cmd = '3';
8176 break;
8177 } else {
8178 /* Otherwise 1 is to save the existing message */
8179 if (option_verbose > 2)
8180 ast_verbose(VERBOSE_PREFIX_3 "Saving message as is\n");
8181 if (!outsidecaller)
8182 ast_filerename(tempfile, recordfile, NULL);
8183 ast_stream_and_wait(chan, "vm-msgsaved", chan->language, "");
8184 if (!outsidecaller) {
8185 STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms);
8186 DISPOSE(recordfile, -1);
8188 cmd = 't';
8189 return res;
8191 case '2':
8192 /* Review */
8193 if (option_verbose > 2)
8194 ast_verbose(VERBOSE_PREFIX_3 "Reviewing the message\n");
8195 cmd = ast_stream_and_wait(chan, tempfile, chan->language, AST_DIGIT_ANY);
8196 break;
8197 case '3':
8198 message_exists = 0;
8199 /* Record */
8200 if (recorded == 1) {
8201 if (option_verbose > 2)
8202 ast_verbose(VERBOSE_PREFIX_3 "Re-recording the message\n");
8203 } else {
8204 if (option_verbose > 2)
8205 ast_verbose(VERBOSE_PREFIX_3 "Recording the message\n");
8207 if (recorded && outsidecaller) {
8208 cmd = ast_play_and_wait(chan, INTRO);
8209 cmd = ast_play_and_wait(chan, "beep");
8211 recorded = 1;
8212 /* 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 */
8213 if (record_gain)
8214 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
8215 if (ast_test_flag(vmu, VM_OPERATOR))
8216 canceldtmf = "0";
8217 cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
8218 if (record_gain)
8219 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
8220 if (cmd == -1) {
8221 /* User has hung up, no options to give */
8222 if (!outsidecaller) {
8223 /* user was recording a greeting and they hung up, so let's delete the recording. */
8224 ast_filedelete(tempfile, NULL);
8226 return cmd;
8228 if (cmd == '0') {
8229 break;
8230 } else if (cmd == '*') {
8231 break;
8233 #if 0
8234 else if (vmu->review && (*duration < 5)) {
8235 /* Message is too short */
8236 if (option_verbose > 2)
8237 ast_verbose(VERBOSE_PREFIX_3 "Message too short\n");
8238 cmd = ast_play_and_wait(chan, "vm-tooshort");
8239 cmd = ast_filedelete(tempfile, NULL);
8240 break;
8242 else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
8243 /* Message is all silence */
8244 if (option_verbose > 2)
8245 ast_verbose(VERBOSE_PREFIX_3 "Nothing recorded\n");
8246 cmd = ast_filedelete(tempfile, NULL);
8247 cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
8248 if (!cmd)
8249 cmd = ast_play_and_wait(chan, "vm-speakup");
8250 break;
8252 #endif
8253 else {
8254 /* If all is well, a message exists */
8255 message_exists = 1;
8256 cmd = 0;
8258 break;
8259 case '4':
8260 case '5':
8261 case '6':
8262 case '7':
8263 case '8':
8264 case '9':
8265 case '*':
8266 case '#':
8267 cmd = ast_play_and_wait(chan, "vm-sorry");
8268 break;
8269 #if 0
8270 /* XXX Commented out for the moment because of the dangers of deleting
8271 a message while recording (can put the message numbers out of sync) */
8272 case '*':
8273 /* Cancel recording, delete message, offer to take another message*/
8274 cmd = ast_play_and_wait(chan, "vm-deleted");
8275 cmd = ast_filedelete(tempfile, NULL);
8276 if (outsidecaller) {
8277 res = vm_exec(chan, NULL);
8278 return res;
8280 else
8281 return 1;
8282 #endif
8283 case '0':
8284 if (!ast_test_flag(vmu, VM_OPERATOR)) {
8285 cmd = ast_play_and_wait(chan, "vm-sorry");
8286 break;
8288 if (message_exists || recorded) {
8289 cmd = ast_play_and_wait(chan, "vm-saveoper");
8290 if (!cmd)
8291 cmd = ast_waitfordigit(chan, 3000);
8292 if (cmd == '1') {
8293 ast_play_and_wait(chan, "vm-msgsaved");
8294 cmd = '0';
8295 } else {
8296 ast_play_and_wait(chan, "vm-deleted");
8297 DELETE(recordfile, -1, recordfile);
8298 cmd = '0';
8301 return cmd;
8302 default:
8303 /* If the caller is an ouside caller, and the review option is enabled,
8304 allow them to review the message, but let the owner of the box review
8305 their OGM's */
8306 if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
8307 return cmd;
8308 if (message_exists) {
8309 cmd = ast_play_and_wait(chan, "vm-review");
8311 else {
8312 cmd = ast_play_and_wait(chan, "vm-torerecord");
8313 if (!cmd)
8314 cmd = ast_waitfordigit(chan, 600);
8317 if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
8318 cmd = ast_play_and_wait(chan, "vm-reachoper");
8319 if (!cmd)
8320 cmd = ast_waitfordigit(chan, 600);
8322 #if 0
8323 if (!cmd)
8324 cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
8325 #endif
8326 if (!cmd)
8327 cmd = ast_waitfordigit(chan, 6000);
8328 if (!cmd) {
8329 attempts++;
8331 if (attempts > max_attempts) {
8332 cmd = 't';
8336 if (outsidecaller)
8337 ast_play_and_wait(chan, "vm-goodbye");
8338 if (cmd == 't')
8339 cmd = 0;
8340 return cmd;
8343 #ifdef IMAP_STORAGE
8345 static void write_file(char *filename, char *buffer, unsigned long len)
8347 FILE *output;
8349 output = fopen (filename, "w");
8350 fwrite (buffer, len, 1, output);
8351 fclose (output);
8354 void mm_searched(MAILSTREAM *stream, unsigned long number)
8356 struct vm_state *vms;
8357 char *mailbox;
8358 char *user;
8359 mailbox = stream->mailbox;
8360 user = get_user_by_mailbox(mailbox);
8361 vms = get_vm_state_by_imapuser(user,2);
8362 if (vms) {
8363 if(option_debug > 2)
8364 ast_log (LOG_DEBUG, "saving mailbox message number %lu as message %d. Interactive set to %d\n",number,vms->vmArrayIndex,vms->interactive);
8365 vms->msgArray[vms->vmArrayIndex++] = number;
8366 } else {
8367 ast_log (LOG_ERROR, "No state found.\n");
8372 #if 0 /*No need for this. */
8373 /* MM status report
8374 * Accepts: MAIL stream
8376 static void status(MAILSTREAM *stream)
8378 unsigned long i;
8379 char *s, date[MAILTMPLEN];
8380 THREADER *thr;
8381 AUTHENTICATOR *auth;
8382 rfc822_date (date);
8383 ast_log (LOG_NOTICE,"%s\n",date);
8384 if (stream) {
8385 if (stream->mailbox)
8386 ast_log (LOG_NOTICE," %s mailbox: %s, %lu messages, %lu recent\n",
8387 stream->dtb->name, stream->mailbox, stream->nmsgs,stream->recent);
8388 else
8389 ast_log (LOG_NOTICE,"No mailbox is open on this stream\n");
8390 if (stream->user_flags[0]) {
8391 ast_log (LOG_NOTICE,"Keywords: %s\n", stream->user_flags[0]);
8392 for (i = 1; i < NUSERFLAGS && stream->user_flags[i]; ++i)
8393 ast_log (LOG_NOTICE," %s\n", stream->user_flags[i]);
8395 if (!strcmp (stream->dtb->name, "imap")) {
8396 if (LEVELIMAP4rev1 (stream))
8397 s = "IMAP4rev1 (RFC 3501)";
8398 else if (LEVEL1730 (stream))
8399 s = "IMAP4 (RFC 1730)";
8400 else if (LEVELIMAP2bis (stream))
8401 s = "IMAP2bis";
8402 else if (LEVEL1176 (stream))
8403 s = "IMAP2 (RFC 1176)";
8404 else
8405 s = "IMAP2 (RFC 1064)";
8406 ast_log (LOG_NOTICE,"%s server %s\n", s, imap_host (stream));
8407 if (LEVELIMAP4 (stream)) {
8408 if ((i = (imap_cap(stream)->auth))) {
8409 s = "";
8410 ast_log (LOG_NOTICE,"Mutually-supported SASL mechanisms:\n");
8411 while ((auth = mail_lookup_auth (find_rightmost_bit (&i) + 1))) {
8412 ast_log (LOG_NOTICE," %s\n", auth->name);
8413 if (!strcmp (auth->name, "PLAIN"))
8414 s = "\n [LOGIN will not be listed here if PLAIN is supported]\n";
8416 ast_log (LOG_NOTICE,s);
8418 ast_log (LOG_NOTICE,"Supported standard extensions:\n");
8419 if (LEVELACL (stream))
8420 ast_log (LOG_NOTICE," Access Control lists (RFC 2086)\n");
8421 if (LEVELQUOTA (stream))
8422 ast_log (LOG_NOTICE," Quotas (RFC 2087)\n");
8423 if (LEVELLITERALPLUS (stream))
8424 ast_log (LOG_NOTICE," Non-synchronizing literals (RFC 2088)\n");
8425 if (LEVELIDLE (stream))
8426 ast_log (LOG_NOTICE," IDLE unsolicited update (RFC 2177)\n");
8427 if (LEVELMBX_REF (stream))
8428 ast_log (LOG_NOTICE," Mailbox referrals (RFC 2193)\n");
8429 if (LEVELLOG_REF (stream))
8430 ast_log (LOG_NOTICE," Login referrals (RFC 2221)\n");
8431 if (LEVELANONYMOUS (stream))
8432 ast_log (LOG_NOTICE," Anonymous access (RFC 2245)\n");
8433 if (LEVELNAMESPACE (stream))
8434 ast_log (LOG_NOTICE," Multiple namespaces (RFC 2342)\n");
8435 if (LEVELUIDPLUS (stream))
8436 ast_log (LOG_NOTICE," Extended UID behavior (RFC 2359)\n");
8437 if (LEVELSTARTTLS (stream))
8438 ast_log (LOG_NOTICE," Transport Layer Security (RFC 2595)\n");
8439 if (LEVELLOGINDISABLED (stream))
8440 ast_log (LOG_NOTICE," LOGIN command disabled (RFC 2595)\n");
8441 if (LEVELID (stream))
8442 ast_log (LOG_NOTICE," Implementation identity negotiation (RFC 2971)\n");
8443 if (LEVELCHILDREN (stream))
8444 ast_log (LOG_NOTICE," LIST children announcement (RFC 3348)\n");
8445 if (LEVELMULTIAPPEND (stream))
8446 ast_log (LOG_NOTICE," Atomic multiple APPEND (RFC 3502)\n");
8447 if (LEVELBINARY (stream))
8448 ast_log (LOG_NOTICE," Binary body content (RFC 3516)\n");
8449 ast_log (LOG_NOTICE,"Supported draft extensions:\n");
8450 if (LEVELUNSELECT (stream))
8451 ast_log (LOG_NOTICE," Mailbox unselect\n");
8452 if (LEVELSASLIR (stream))
8453 ast_log (LOG_NOTICE," SASL initial client response\n");
8454 if (LEVELSORT (stream))
8455 ast_log (LOG_NOTICE," Server-based sorting\n");
8456 if (LEVELTHREAD (stream)) {
8457 ast_log (LOG_NOTICE," Server-based threading:\n");
8458 for (thr = imap_cap(stream)->threader; thr; thr = thr->next)
8459 ast_log (LOG_NOTICE," %s\n", thr->name);
8461 if (LEVELSCAN (stream))
8462 ast_log (LOG_NOTICE," Mailbox text scan\n");
8463 if ((i = imap_cap(stream)->extlevel)) {
8464 ast_log (LOG_NOTICE,"Supported BODYSTRUCTURE extensions:\n");
8465 switch (i) {
8466 case BODYEXTLOC:
8467 ast_log (LOG_NOTICE," location\n");
8468 case BODYEXTLANG:
8469 ast_log (LOG_NOTICE," language\n");
8470 case BODYEXTDSP:
8471 ast_log (LOG_NOTICE," disposition\n");
8472 case BODYEXTMD5:
8473 ast_log (LOG_NOTICE," MD5\n");
8476 }else
8477 ast_log (LOG_NOTICE,"\n");
8481 #endif
8483 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
8485 struct ast_variable *var;
8486 struct ast_vm_user *vmu;
8488 vmu = ast_calloc(1, sizeof *vmu);
8489 if (!vmu)
8490 return NULL;
8491 ast_set_flag(vmu, VM_ALLOCED);
8492 populate_defaults(vmu);
8494 var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
8495 if (var) {
8496 apply_options_full(vmu, var);
8497 ast_variables_destroy(var);
8498 return vmu;
8499 } else {
8500 free(vmu);
8501 return NULL;
8505 /* Interfaces to C-client */
8507 void mm_exists(MAILSTREAM * stream, unsigned long number)
8509 /* mail_ping will callback here if new mail! */
8510 if(option_debug > 3)
8511 ast_log (LOG_DEBUG, "Entering EXISTS callback for message %ld\n", number);
8512 if (number == 0) return;
8513 set_update(stream);
8517 void mm_expunged(MAILSTREAM * stream, unsigned long number)
8519 /* mail_ping will callback here if expunged mail! */
8520 if(option_debug > 3)
8521 ast_log (LOG_DEBUG, "Entering EXPUNGE callback for message %ld\n", number);
8522 if (number == 0) return;
8523 set_update(stream);
8527 void mm_flags(MAILSTREAM * stream, unsigned long number)
8529 /* mail_ping will callback here if read mail! */
8530 if(option_debug > 3)
8531 ast_log (LOG_DEBUG, "Entering FLAGS callback for message %ld\n", number);
8532 if (number == 0) return;
8533 set_update(stream);
8537 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
8539 mm_log (string, errflg);
8543 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
8545 if (delimiter == '\0') {
8546 delimiter = delim;
8548 if (option_debug > 4) {
8549 ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
8550 if (attributes & LATT_NOINFERIORS)
8551 ast_log(LOG_DEBUG, "no inferiors\n");
8552 if (attributes & LATT_NOSELECT)
8553 ast_log(LOG_DEBUG, "no select\n");
8554 if (attributes & LATT_MARKED)
8555 ast_log(LOG_DEBUG, "marked\n");
8556 if (attributes & LATT_UNMARKED)
8557 ast_log(LOG_DEBUG, "unmarked\n");
8562 void mm_lsub(MAILSTREAM * stream, int delimiter, char *mailbox, long attributes)
8564 if (option_debug > 4) {
8565 ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delimiter, mailbox);
8566 if (attributes & LATT_NOINFERIORS)
8567 ast_log(LOG_DEBUG, "no inferiors\n");
8568 if (attributes & LATT_NOSELECT)
8569 ast_log(LOG_DEBUG, "no select\n");
8570 if (attributes & LATT_MARKED)
8571 ast_log(LOG_DEBUG, "marked\n");
8572 if (attributes & LATT_UNMARKED)
8573 ast_log(LOG_DEBUG, "unmarked\n");
8578 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
8580 ast_log (LOG_NOTICE," Mailbox %s", mailbox);
8581 if (status->flags & SA_MESSAGES)
8582 ast_log (LOG_NOTICE,", %lu messages", status->messages);
8583 if (status->flags & SA_RECENT)
8584 ast_log (LOG_NOTICE,", %lu recent", status->recent);
8585 if (status->flags & SA_UNSEEN)
8586 ast_log (LOG_NOTICE,", %lu unseen", status->unseen);
8587 if (status->flags & SA_UIDVALIDITY)
8588 ast_log (LOG_NOTICE,", %lu UID validity", status->uidvalidity);
8589 if (status->flags & SA_UIDNEXT)
8590 ast_log (LOG_NOTICE,", %lu next UID", status->uidnext);
8591 ast_log (LOG_NOTICE,"\n");
8595 void mm_log(char *string, long errflg)
8597 switch ((short) errflg) {
8598 case NIL:
8599 if(option_debug)
8600 ast_log(LOG_DEBUG,"IMAP Info: %s\n", string);
8601 break;
8602 case PARSE:
8603 case WARN:
8604 ast_log (LOG_WARNING,"IMAP Warning: %s\n", string);
8605 break;
8606 case ERROR:
8607 ast_log (LOG_ERROR,"IMAP Error: %s\n", string);
8608 break;
8613 void mm_dlog(char *string)
8615 ast_log (LOG_NOTICE, "%s\n", string);
8619 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
8621 struct ast_vm_user *vmu;
8623 if(option_debug > 3)
8624 ast_log(LOG_DEBUG, "Entering callback mm_login\n");
8626 ast_copy_string(user, mb->user, MAILTMPLEN);
8628 /* We should only do this when necessary */
8629 if (!ast_strlen_zero(authpassword)) {
8630 ast_copy_string(pwd, authpassword, MAILTMPLEN);
8631 } else {
8632 AST_LIST_TRAVERSE(&users, vmu, list) {
8633 if(!strcasecmp(mb->user, vmu->imapuser)) {
8634 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
8635 break;
8638 if (!vmu) {
8639 if ((vmu = find_user_realtime_imapuser(mb->user))) {
8640 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
8641 free_user(vmu);
8648 void mm_critical(MAILSTREAM * stream)
8653 void mm_nocritical(MAILSTREAM * stream)
8658 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
8660 kill (getpid (), SIGSTOP);
8661 return NIL;
8665 void mm_fatal(char *string)
8667 ast_log(LOG_ERROR,"IMAP access FATAL error: %s\n", string);
8670 /* C-client callback to handle quota */
8671 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
8673 struct vm_state *vms;
8674 char *mailbox;
8675 char *user;
8676 unsigned long usage = 0;
8677 unsigned long limit = 0;
8679 while (pquota) {
8680 usage = pquota->usage;
8681 limit = pquota->limit;
8682 pquota = pquota->next;
8685 mailbox = stream->mailbox;
8686 user = get_user_by_mailbox(mailbox);
8687 vms = get_vm_state_by_imapuser(user,2);
8688 if (vms) {
8689 if(option_debug > 2)
8690 ast_log (LOG_DEBUG, "User %s usage is %lu, limit is %lu\n",user,usage,limit);
8691 vms->quota_usage = usage;
8692 vms->quota_limit = limit;
8693 } else {
8694 ast_log (LOG_ERROR, "No state found.\n");
8698 static char *get_header_by_tag(char *header, char *tag)
8700 char *start;
8701 int taglen;
8702 char *eol_pnt;
8704 if (!header || !tag)
8705 return NULL;
8707 taglen = strlen(tag) + 1;
8708 if (taglen < 1)
8709 return NULL;
8711 start = strstr(header, tag);
8712 if (!start)
8713 return NULL;
8715 ast_mutex_lock(&imaptemp_lock);
8716 ast_copy_string(imaptemp, start+taglen, sizeof(imaptemp));
8717 ast_mutex_unlock(&imaptemp_lock);
8718 if ((eol_pnt = strchr(imaptemp,'\r')) || (eol_pnt = strchr(imaptemp,'\n')))
8719 *eol_pnt = '\0';
8720 return imaptemp;
8723 static char *get_user_by_mailbox(char *mailbox)
8725 char *start, *quote;
8726 char *eol_pnt;
8728 if (!mailbox)
8729 return NULL;
8731 start = strstr(mailbox,"/user=");
8732 if (!start)
8733 return NULL;
8735 ast_mutex_lock(&imaptemp_lock);
8736 ast_copy_string(imaptemp, start+6, sizeof(imaptemp));
8737 ast_mutex_unlock(&imaptemp_lock);
8739 quote = strchr(imaptemp,'\"');
8740 if (!quote) { /* if username is not in quotes */
8741 eol_pnt = strchr(imaptemp,'/');
8742 if (!eol_pnt) {
8743 eol_pnt = strchr(imaptemp,'}');
8745 *eol_pnt = '\0';
8746 return imaptemp;
8747 } else {
8748 eol_pnt = strchr(imaptemp+1,'\"');
8749 *eol_pnt = '\0';
8750 return imaptemp+1;
8754 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
8756 struct vmstate *vlist = NULL;
8758 vlist = vmstates;
8759 while (vlist) {
8760 if (vlist->vms) {
8761 if (vlist->vms->imapuser) {
8762 if (!strcmp(vlist->vms->imapuser,user)) {
8763 if (interactive == 2) {
8764 return vlist->vms;
8765 } else if (vlist->vms->interactive == interactive) {
8766 return vlist->vms;
8769 } else {
8770 if(option_debug > 2)
8771 ast_log(LOG_DEBUG, " error: imapuser is NULL for %s\n",user);
8773 } else {
8774 if(option_debug > 2)
8775 ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",user);
8777 vlist = vlist->next;
8779 if(option_debug > 2)
8780 ast_log(LOG_DEBUG, "%s not found in vmstates\n",user);
8781 return NULL;
8784 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive)
8786 struct vmstate *vlist = NULL;
8788 vlist = vmstates;
8789 if(option_debug > 2)
8790 ast_log(LOG_DEBUG, "Mailbox set to %s\n",mailbox);
8791 while (vlist) {
8792 if (vlist->vms) {
8793 if (vlist->vms->username) {
8794 if(option_debug > 2)
8795 ast_log(LOG_DEBUG, " comparing mailbox %s (i=%d) to vmstate mailbox %s (i=%d)\n",mailbox,interactive,vlist->vms->username,vlist->vms->interactive);
8796 if (!strcmp(vlist->vms->username,mailbox) && vlist->vms->interactive == interactive) {
8797 if(option_debug > 2)
8798 ast_log(LOG_DEBUG, " Found it!\n");
8799 return vlist->vms;
8801 } else {
8802 if(option_debug > 2)
8803 ast_log(LOG_DEBUG, " error: username is NULL for %s\n",mailbox);
8805 } else {
8806 if(option_debug > 2)
8807 ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",mailbox);
8809 vlist = vlist->next;
8811 if(option_debug > 2)
8812 ast_log(LOG_DEBUG, "%s not found in vmstates\n",mailbox);
8813 return NULL;
8816 static void vmstate_insert(struct vm_state *vms)
8818 struct vmstate *v;
8819 struct vm_state *altvms;
8821 /* If interactive, it probably already exists, and we should
8822 use the one we already have since it is more up to date.
8823 We can compare the username to find the duplicate */
8824 if (vms->interactive == 1) {
8825 altvms = get_vm_state_by_mailbox(vms->username,0);
8826 if (altvms) {
8827 if(option_debug > 2)
8828 ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
8829 vms->newmessages = altvms->newmessages;
8830 vms->oldmessages = altvms->oldmessages;
8831 if(option_debug > 2)
8832 ast_log(LOG_DEBUG, "check_msgArray before memcpy\n");
8833 check_msgArray(vms);
8834 /* memcpy(vms->msgArray, altvms->msgArray, sizeof(long)*256); */
8835 copy_msgArray(vms, altvms);
8836 if(option_debug > 2)
8837 ast_log(LOG_DEBUG, "check_msgArray after memcpy\n");
8838 check_msgArray(vms);
8839 vms->vmArrayIndex = altvms->vmArrayIndex;
8840 vms->lastmsg = altvms->lastmsg;
8841 vms->curmsg = altvms->curmsg;
8842 /* get a pointer to the persistent store */
8843 vms->persist_vms = altvms;
8844 /* Reuse the mailstream? */
8845 vms->mailstream = altvms->mailstream;
8846 /* vms->mailstream = NIL; */
8850 v = (struct vmstate *)malloc(sizeof(struct vmstate));
8851 if (!v) {
8852 ast_log(LOG_ERROR, "Out of memory\n");
8854 if(option_debug > 2)
8855 ast_log(LOG_DEBUG, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
8856 ast_mutex_lock(&vmstate_lock);
8857 v->vms = vms;
8858 v->next = vmstates;
8859 vmstates = v;
8860 ast_mutex_unlock(&vmstate_lock);
8863 static void vmstate_delete(struct vm_state *vms)
8865 struct vmstate *vc, *vf = NULL, *vl = NULL;
8866 struct vm_state *altvms;
8868 /* If interactive, we should copy pertainent info
8869 back to the persistent state (to make update immediate) */
8870 if (vms->interactive == 1) {
8871 altvms = vms->persist_vms;
8872 if (altvms) {
8873 if(option_debug > 2)
8874 ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
8875 altvms->newmessages = vms->newmessages;
8876 altvms->oldmessages = vms->oldmessages;
8877 altvms->updated = 1;
8881 ast_mutex_lock(&vmstate_lock);
8882 vc = vmstates;
8883 if(option_debug > 2)
8884 ast_log(LOG_DEBUG, "Removing vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
8885 while (vc) {
8886 if (vc->vms == vms) {
8887 vf = vc;
8888 if (vl)
8889 vl->next = vc->next;
8890 else
8891 vmstates = vc->next;
8892 break;
8894 vl = vc;
8895 vc = vc->next;
8897 if (!vf) {
8898 ast_log(LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n",vms->imapuser,vms->username);
8899 } else {
8900 free(vf);
8902 ast_mutex_unlock(&vmstate_lock);
8905 static void set_update(MAILSTREAM * stream)
8907 struct vm_state *vms;
8908 char *mailbox;
8909 char *user;
8911 mailbox = stream->mailbox;
8912 user = get_user_by_mailbox(mailbox);
8913 vms = get_vm_state_by_imapuser(user, 0);
8914 if (vms) {
8915 if(option_debug > 2)
8916 ast_log (LOG_DEBUG, "User %s mailbox set for update.\n",user);
8917 vms->updated = 1; /* set updated flag since mailbox changed */
8918 } else {
8919 if(option_debug > 2)
8920 ast_log (LOG_WARNING, "User %s mailbox not found for update.\n",user);
8924 static void init_vm_state(struct vm_state *vms)
8926 int x;
8927 vms->vmArrayIndex = 0;
8928 for (x = 0; x < 256; x++) {
8929 vms->msgArray[x] = 0;
8933 static void check_msgArray(struct vm_state *vms)
8935 int x;
8936 for (x = 0; x<256; x++) {
8937 if (vms->msgArray[x]!=0) {
8938 if(option_debug)
8939 ast_log (LOG_DEBUG, "Item %d set to %ld\n",x,vms->msgArray[x]);
8944 static void copy_msgArray(struct vm_state *dst, struct vm_state *src)
8946 int x;
8947 for (x = 0; x<256; x++) {
8948 dst->msgArray[x] = src->msgArray[x];
8952 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format)
8954 char *body_content;
8955 char *body_decoded;
8956 unsigned long len;
8957 unsigned long newlen;
8958 char filename[256];
8960 if (!body || body == NIL)
8961 return -1;
8962 body_content = mail_fetchbody (vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
8963 if (body_content != NIL) {
8964 snprintf(filename, sizeof(filename), "%s.%s", vms->fn, format);
8965 /* ast_log (LOG_DEBUG,body_content); */
8966 body_decoded = rfc822_base64 ((unsigned char *)body_content, len, &newlen);
8967 write_file (filename, (char *) body_decoded, newlen);
8969 return 0;
8972 /* get delimiter via mm_list callback */
8973 static void get_mailbox_delimiter(MAILSTREAM *stream) {
8974 char tmp[50];
8975 snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
8976 mail_list(stream, tmp, "*");
8979 #endif /* IMAP_STORAGE */
8981 /* This is a workaround so that menuselect displays a proper description
8982 * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
8985 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
8986 .load = load_module,
8987 .unload = unload_module,
8988 .reload = reload,