Remove debug output.
[asterisk-bristuff.git] / apps / app_voicemail.c
blob8118a49e010a745f698462efc78e89cd3838ebc7
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
19 /*! \file
21 * \brief Comedian Mail - Voicemail System
23 * \author Mark Spencer <markster@digium.com>
25 * \par See also
26 * \arg \ref Config_vm
27 * \ingroup applications
28 * \note This module requires res_adsi to load.
31 /*** MODULEINFO
32 <depend>res_adsi</depend>
33 <depend>res_smdi</depend>
34 ***/
36 /*** MAKEOPTS
37 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o apps/app_directory.o">
38 <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
39 <depend>unixodbc</depend>
40 <depend>ltdl</depend>
41 <conflict>IMAP_STORAGE</conflict>
42 <defaultenabled>no</defaultenabled>
43 </member>
44 <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
45 <depend>imap_tk</depend>
46 <conflict>ODBC_STORAGE</conflict>
47 <use>ssl</use>
48 <defaultenabled>no</defaultenabled>
49 </member>
50 </category>
51 ***/
53 #include "asterisk.h"
55 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
57 #include <stdlib.h>
58 #include <errno.h>
59 #include <unistd.h>
60 #include <string.h>
61 #include <stdlib.h>
62 #include <stdio.h>
63 #include <sys/time.h>
64 #include <sys/stat.h>
65 #include <sys/types.h>
66 #include <sys/mman.h>
67 #include <time.h>
68 #include <dirent.h>
69 #ifdef IMAP_STORAGE
70 #include <ctype.h>
71 #include <signal.h>
72 #include <pwd.h>
73 #ifdef USE_SYSTEM_IMAP
74 #include <imap/c-client.h>
75 #include <imap/imap4r1.h>
76 #include <imap/linkage.h>
77 #elif defined (USE_SYSTEM_CCLIENT)
78 #include <c-client/c-client.h>
79 #include <c-client/imap4r1.h>
80 #include <c-client/linkage.h>
81 #else
82 #include "c-client.h"
83 #include "imap4r1.h"
84 #include "linkage.h"
85 #endif
86 #endif
87 #include "asterisk/lock.h"
88 #include "asterisk/file.h"
89 #include "asterisk/logger.h"
90 #include "asterisk/channel.h"
91 #include "asterisk/pbx.h"
92 #include "asterisk/options.h"
93 #include "asterisk/config.h"
94 #include "asterisk/say.h"
95 #include "asterisk/module.h"
96 #include "asterisk/adsi.h"
97 #include "asterisk/app.h"
98 #include "asterisk/manager.h"
99 #include "asterisk/dsp.h"
100 #include "asterisk/localtime.h"
101 #include "asterisk/cli.h"
102 #include "asterisk/utils.h"
103 #include "asterisk/stringfields.h"
104 #include "asterisk/smdi.h"
105 #ifdef ODBC_STORAGE
106 #include "asterisk/res_odbc.h"
107 #endif
109 #ifdef IMAP_STORAGE
110 AST_MUTEX_DEFINE_STATIC(imaptemp_lock);
111 static char imaptemp[1024];
113 static char imapserver[48];
114 static char imapport[8];
115 static char imapflags[128];
116 static char imapfolder[64];
117 static char authuser[32];
118 static char authpassword[42];
120 static int expungeonhangup = 1;
121 static char delimiter = '\0';
123 struct vm_state;
124 struct ast_vm_user;
126 static int init_mailstream (struct vm_state *vms, int box);
127 static void write_file (char *filename, char *buffer, unsigned long len);
128 /*static void status (MAILSTREAM *stream); */ /* No need for this. */
129 static char *get_header_by_tag(char *header, char *tag);
130 static void vm_imap_delete(int msgnum, struct vm_state *vms);
131 static char *get_user_by_mailbox(char *mailbox);
132 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
133 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
134 static void vmstate_insert(struct vm_state *vms);
135 static void vmstate_delete(struct vm_state *vms);
136 static void set_update(MAILSTREAM * stream);
137 static void init_vm_state(struct vm_state *vms);
138 static void check_msgArray(struct vm_state *vms);
139 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
140 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
141 static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num);
142 static void get_mailbox_delimiter(MAILSTREAM *stream);
143 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
144 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
145 static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms);
146 static void check_quota(struct vm_state *vms, char *mailbox);
147 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box);
148 struct vmstate {
149 struct vm_state *vms;
150 struct vmstate *next;
152 AST_MUTEX_DEFINE_STATIC(vmstate_lock);
153 static struct vmstate *vmstates = NULL;
154 #endif
156 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
158 #define COMMAND_TIMEOUT 5000
159 /* Don't modify these here; set your umask at runtime instead */
160 #define VOICEMAIL_DIR_MODE 0777
161 #define VOICEMAIL_FILE_MODE 0666
162 #define CHUNKSIZE 65536
164 #define VOICEMAIL_CONFIG "voicemail.conf"
165 #define ASTERISK_USERNAME "asterisk"
167 /* Default mail command to mail voicemail. Change it with the
168 mailcmd= command in voicemail.conf */
169 #define SENDMAIL "/usr/sbin/sendmail -t"
171 #define INTRO "vm-intro"
173 #define MAXMSG 100
174 #ifndef IMAP_STORAGE
175 #define MAXMSGLIMIT 9999
176 #else
177 #define MAXMSGLIMIT 255
178 #endif
180 #define BASEMAXINLINE 256
181 #define BASELINELEN 72
182 #define BASEMAXINLINE 256
183 #define eol "\r\n"
185 #define MAX_DATETIME_FORMAT 512
186 #define MAX_NUM_CID_CONTEXTS 10
188 #define VM_REVIEW (1 << 0)
189 #define VM_OPERATOR (1 << 1)
190 #define VM_SAYCID (1 << 2)
191 #define VM_SVMAIL (1 << 3)
192 #define VM_ENVELOPE (1 << 4)
193 #define VM_SAYDURATION (1 << 5)
194 #define VM_SKIPAFTERCMD (1 << 6)
195 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
196 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
197 #define VM_PBXSKIP (1 << 9)
198 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
199 #define VM_ATTACH (1 << 11)
200 #define VM_DELETE (1 << 12)
201 #define VM_ALLOCED (1 << 13)
202 #define VM_SEARCH (1 << 14)
203 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
204 #define ERROR_LOCK_PATH -100
205 #define ERROR_MAILBOX_FULL -200
208 enum {
209 OPT_SILENT = (1 << 0),
210 OPT_BUSY_GREETING = (1 << 1),
211 OPT_UNAVAIL_GREETING = (1 << 2),
212 OPT_RECORDGAIN = (1 << 3),
213 OPT_PREPEND_MAILBOX = (1 << 4),
214 OPT_PRIORITY_JUMP = (1 << 5),
215 OPT_AUTOPLAY = (1 << 6),
216 } vm_option_flags;
218 enum {
219 OPT_ARG_RECORDGAIN = 0,
220 OPT_ARG_PLAYFOLDER = 1,
221 /* This *must* be the last value in this enum! */
222 OPT_ARG_ARRAY_SIZE = 2,
223 } vm_option_args;
225 AST_APP_OPTIONS(vm_app_options, {
226 AST_APP_OPTION('s', OPT_SILENT),
227 AST_APP_OPTION('b', OPT_BUSY_GREETING),
228 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
229 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
230 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
231 AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
232 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
235 static int load_config(void);
237 /*! \page vmlang Voicemail Language Syntaxes Supported
239 \par Syntaxes supported, not really language codes.
240 \arg \b en - English
241 \arg \b de - German
242 \arg \b es - Spanish
243 \arg \b fr - French
244 \arg \b it = Italian
245 \arg \b nl - Dutch
246 \arg \b pt - Polish
247 \arg \b pt - Portuguese
248 \arg \b pt_BR - Portuguese (Brazil)
249 \arg \b gr - Greek
250 \arg \b no - Norwegian
251 \arg \b se - Swedish
252 \arg \b ua - Ukrainian
254 German requires the following additional soundfile:
255 \arg \b 1F einE (feminine)
257 Spanish requires the following additional soundfile:
258 \arg \b 1M un (masculine)
260 Dutch, Portuguese & Spanish require the following additional soundfiles:
261 \arg \b vm-INBOXs singular of 'new'
262 \arg \b vm-Olds singular of 'old/heard/read'
264 NB these are plural:
265 \arg \b vm-INBOX nieuwe (nl)
266 \arg \b vm-Old oude (nl)
268 Polish uses:
269 \arg \b vm-new-a 'new', feminine singular accusative
270 \arg \b vm-new-e 'new', feminine plural accusative
271 \arg \b vm-new-ych 'new', feminine plural genitive
272 \arg \b vm-old-a 'old', feminine singular accusative
273 \arg \b vm-old-e 'old', feminine plural accusative
274 \arg \b vm-old-ych 'old', feminine plural genitive
275 \arg \b digits/1-a 'one', not always same as 'digits/1'
276 \arg \b digits/2-ie 'two', not always same as 'digits/2'
278 Swedish uses:
279 \arg \b vm-nytt singular of 'new'
280 \arg \b vm-nya plural of 'new'
281 \arg \b vm-gammalt singular of 'old'
282 \arg \b vm-gamla plural of 'old'
283 \arg \b digits/ett 'one', not always same as 'digits/1'
285 Norwegian uses:
286 \arg \b vm-ny singular of 'new'
287 \arg \b vm-nye plural of 'new'
288 \arg \b vm-gammel singular of 'old'
289 \arg \b vm-gamle plural of 'old'
291 Dutch also uses:
292 \arg \b nl-om 'at'?
294 Spanish also uses:
295 \arg \b vm-youhaveno
297 Ukrainian requires the following additional soundfile:
298 \arg \b vm-nove 'nove'
299 \arg \b vm-stare 'stare'
300 \arg \b digits/ua/1e 'odne'
302 Italian requires the following additional soundfile:
304 For vm_intro_it:
305 \arg \b vm-nuovo new
306 \arg \b vm-nuovi new plural
307 \arg \b vm-vecchio old
308 \arg \b vm-vecchi old plural
310 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
311 spelled among others when you have to change folder. For the above reasons, vm-INBOX
312 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
316 struct baseio {
317 int iocp;
318 int iolen;
319 int linelength;
320 int ateof;
321 unsigned char iobuf[BASEMAXINLINE];
324 /*! Structure for linked list of users */
325 struct ast_vm_user {
326 char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
327 char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
328 char password[80]; /*!< Secret pin code, numbers only */
329 char fullname[80]; /*!< Full name, for directory app */
330 char email[80]; /*!< E-mail address */
331 char pager[80]; /*!< E-mail address to pager (no attachment) */
332 char serveremail[80]; /*!< From: Mail address */
333 char mailcmd[160]; /*!< Configurable mail command */
334 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
335 char zonetag[80]; /*!< Time zone */
336 char callback[80];
337 char dialout[80];
338 char uniqueid[80]; /*!< Unique integer identifier */
339 char exit[80];
340 char attachfmt[20]; /*!< Attachment format */
341 unsigned int flags; /*!< VM_ flags */
342 int saydurationm;
343 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
344 #ifdef IMAP_STORAGE
345 char imapuser[80]; /* IMAP server login */
346 char imappassword[80]; /* IMAP server password if authpassword not defined */
347 #endif
348 double volgain; /*!< Volume gain for voicemails sent via email */
349 AST_LIST_ENTRY(ast_vm_user) list;
352 struct vm_zone {
353 AST_LIST_ENTRY(vm_zone) list;
354 char name[80];
355 char timezone[80];
356 char msg_format[512];
359 struct vm_state {
360 char curbox[80];
361 char username[80];
362 char curdir[PATH_MAX];
363 char vmbox[PATH_MAX];
364 char fn[PATH_MAX];
365 char fn2[PATH_MAX];
366 int *deleted;
367 int *heard;
368 int curmsg;
369 int lastmsg;
370 int newmessages;
371 int oldmessages;
372 int starting;
373 int repeats;
374 #ifdef IMAP_STORAGE
375 ast_mutex_t lock;
376 int updated; /* decremented on each mail check until 1 -allows delay */
377 long msgArray[256];
378 MAILSTREAM *mailstream;
379 int vmArrayIndex;
380 char imapuser[80]; /* IMAP server login */
381 int interactive;
382 unsigned int quota_limit;
383 unsigned int quota_usage;
384 struct vm_state *persist_vms;
385 #endif
387 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
388 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
389 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
390 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
391 signed char record_gain, struct vm_state *vms);
392 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
393 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
394 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
395 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap);
396 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
397 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
398 #endif
399 static void apply_options(struct ast_vm_user *vmu, const char *options);
401 #ifdef ODBC_STORAGE
402 static char odbc_database[80];
403 static char odbc_table[80];
404 #define RETRIEVE(a,b) retrieve_file(a,b)
405 #define DISPOSE(a,b) remove_file(a,b)
406 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
407 #define EXISTS(a,b,c,d) (message_exists(a,b))
408 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
409 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
410 #define DELETE(a,b,c) (delete_file(a,b))
411 #else
412 #ifdef IMAP_STORAGE
413 #define RETRIEVE(a,b)
414 #define DISPOSE(a,b)
415 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
416 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
417 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
418 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
419 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
420 #define DELETE(a,b,c) (vm_delete(c))
421 #else
422 #define RETRIEVE(a,b)
423 #define DISPOSE(a,b)
424 #define STORE(a,b,c,d,e,f,g,h,i)
425 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
426 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
427 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
428 #define DELETE(a,b,c) (vm_delete(c))
429 #endif
430 #endif
432 static char VM_SPOOL_DIR[PATH_MAX];
434 static char ext_pass_cmd[128];
436 int my_umask;
438 #if ODBC_STORAGE
439 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
440 #elif IMAP_STORAGE
441 #define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
442 #else
443 #define tdesc "Comedian Mail (Voicemail System)"
444 #endif
446 static char userscontext[AST_MAX_EXTENSION] = "default";
448 static char *addesc = "Comedian Mail";
450 static char *synopsis_vm =
451 "Leave a Voicemail message";
453 static char *descrip_vm =
454 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
455 "application allows the calling party to leave a message for the specified\n"
456 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
457 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
458 "specified mailbox does not exist.\n"
459 " The Voicemail application will exit if any of the following DTMF digits are\n"
460 "received:\n"
461 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
462 " * - Jump to the 'a' extension in the current dialplan context.\n"
463 " This application will set the following channel variable upon completion:\n"
464 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
465 " application. The possible values are:\n"
466 " SUCCESS | USEREXIT | FAILED\n\n"
467 " Options:\n"
468 " b - Play the 'busy' greeting to the calling party.\n"
469 " g(#) - Use the specified amount of gain when recording the voicemail\n"
470 " message. The units are whole-number decibels (dB).\n"
471 " s - Skip the playback of instructions for leaving a message to the\n"
472 " calling party.\n"
473 " u - Play the 'unavailable' greeting.\n"
474 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
475 " error occurs.\n";
477 static char *synopsis_vmain =
478 "Check Voicemail messages";
480 static char *descrip_vmain =
481 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
482 "calling party to check voicemail messages. A specific mailbox, and optional\n"
483 "corresponding context, may be specified. If a mailbox is not provided, the\n"
484 "calling party will be prompted to enter one. If a context is not specified,\n"
485 "the 'default' context will be used.\n\n"
486 " Options:\n"
487 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
488 " is entered by the caller.\n"
489 " g(#) - Use the specified amount of gain when recording a voicemail\n"
490 " message. The units are whole-number decibels (dB).\n"
491 " s - Skip checking the passcode for the mailbox.\n"
492 " a(#) - Skip folder prompt and go directly to folder specified.\n"
493 " Defaults to INBOX\n";
495 static char *synopsis_vm_box_exists =
496 "Check to see if Voicemail mailbox exists";
498 static char *descrip_vm_box_exists =
499 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
500 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
501 "will be used.\n"
502 " This application will set the following channel variable upon completion:\n"
503 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
504 " MailboxExists application. Possible values include:\n"
505 " SUCCESS | FAILED\n\n"
506 " Options:\n"
507 " j - Jump to priority n+101 if the mailbox is found.\n";
509 static char *synopsis_vmauthenticate =
510 "Authenticate with Voicemail passwords";
512 static char *descrip_vmauthenticate =
513 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
514 "same way as the Authenticate application, but the passwords are taken from\n"
515 "voicemail.conf.\n"
516 " If the mailbox is specified, only that mailbox's password will be considered\n"
517 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
518 "be set with the authenticated mailbox.\n\n"
519 " Options:\n"
520 " s - Skip playing the initial prompts.\n";
522 /* Leave a message */
523 static char *app = "VoiceMail";
525 /* Check mail, control, etc */
526 static char *app2 = "VoiceMailMain";
528 static char *app3 = "MailboxExists";
529 static char *app4 = "VMAuthenticate";
531 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
532 static AST_LIST_HEAD_STATIC(zones, vm_zone);
533 static int maxsilence;
534 static int maxmsg;
535 static int silencethreshold = 128;
536 static char serveremail[80];
537 static char mailcmd[160]; /* Configurable mail cmd */
538 static char externnotify[160];
539 static struct ast_smdi_interface *smdi_iface = NULL;
540 static char vmfmts[80];
541 static double volgain;
542 static int vmminmessage;
543 static int vmmaxmessage;
544 static int maxgreet;
545 static int skipms;
546 static int maxlogins;
548 static struct ast_flags globalflags = {0};
550 static int saydurationminfo;
552 static char dialcontext[AST_MAX_CONTEXT];
553 static char callcontext[AST_MAX_CONTEXT];
554 static char exitcontext[AST_MAX_CONTEXT];
556 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
559 static char *emailbody = NULL;
560 static char *emailsubject = NULL;
561 static char *pagerbody = NULL;
562 static char *pagersubject = NULL;
563 static char fromstring[100];
564 static char pagerfromstring[100];
565 static char emailtitle[100];
566 static char charset[32] = "ISO-8859-1";
568 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
569 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
570 static int adsiver = 1;
571 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
574 static void populate_defaults(struct ast_vm_user *vmu)
576 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
577 if (saydurationminfo)
578 vmu->saydurationm = saydurationminfo;
579 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
580 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
581 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
582 if (maxmsg)
583 vmu->maxmsg = maxmsg;
584 vmu->volgain = volgain;
587 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
589 int x;
590 if (!strcasecmp(var, "attach")) {
591 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
592 } else if (!strcasecmp(var, "attachfmt")) {
593 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
594 } else if (!strcasecmp(var, "serveremail")) {
595 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
596 } else if (!strcasecmp(var, "language")) {
597 ast_copy_string(vmu->language, value, sizeof(vmu->language));
598 } else if (!strcasecmp(var, "tz")) {
599 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
600 #ifdef IMAP_STORAGE
601 } else if (!strcasecmp(var, "imapuser")) {
602 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
603 } else if (!strcasecmp(var, "imappassword")) {
604 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
605 #endif
606 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
607 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
608 } else if (!strcasecmp(var, "saycid")){
609 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
610 } else if (!strcasecmp(var,"sendvoicemail")){
611 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
612 } else if (!strcasecmp(var, "review")){
613 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
614 } else if (!strcasecmp(var, "tempgreetwarn")){
615 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
616 } else if (!strcasecmp(var, "operator")){
617 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
618 } else if (!strcasecmp(var, "envelope")){
619 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
620 } else if (!strcasecmp(var, "sayduration")){
621 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
622 } else if (!strcasecmp(var, "saydurationm")){
623 if (sscanf(value, "%d", &x) == 1) {
624 vmu->saydurationm = x;
625 } else {
626 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
628 } else if (!strcasecmp(var, "forcename")){
629 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
630 } else if (!strcasecmp(var, "forcegreetings")){
631 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
632 } else if (!strcasecmp(var, "callback")) {
633 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
634 } else if (!strcasecmp(var, "dialout")) {
635 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
636 } else if (!strcasecmp(var, "exitcontext")) {
637 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
638 } else if (!strcasecmp(var, "maxmsg")) {
639 vmu->maxmsg = atoi(value);
640 if (vmu->maxmsg <= 0) {
641 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
642 vmu->maxmsg = MAXMSG;
643 } else if (vmu->maxmsg > MAXMSGLIMIT) {
644 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
645 vmu->maxmsg = MAXMSGLIMIT;
647 } else if (!strcasecmp(var, "volgain")) {
648 sscanf(value, "%lf", &vmu->volgain);
649 } else if (!strcasecmp(var, "options")) {
650 apply_options(vmu, value);
654 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
656 int res;
657 if (!ast_strlen_zero(vmu->uniqueid)) {
658 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
659 if (res > 0) {
660 ast_copy_string(vmu->password, password, sizeof(vmu->password));
661 res = 0;
662 } else if (!res) {
663 res = -1;
665 return res;
667 return -1;
670 static void apply_options(struct ast_vm_user *vmu, const char *options)
671 { /* Destructively Parse options and apply */
672 char *stringp;
673 char *s;
674 char *var, *value;
675 stringp = ast_strdupa(options);
676 while ((s = strsep(&stringp, "|"))) {
677 value = s;
678 if ((var = strsep(&value, "=")) && value) {
679 apply_option(vmu, var, value);
684 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
686 struct ast_variable *tmp;
687 tmp = var;
688 while (tmp) {
689 if (!strcasecmp(tmp->name, "vmsecret")) {
690 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
691 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
692 if (ast_strlen_zero(retval->password))
693 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
694 } else if (!strcasecmp(tmp->name, "uniqueid")) {
695 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
696 } else if (!strcasecmp(tmp->name, "pager")) {
697 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
698 } else if (!strcasecmp(tmp->name, "email")) {
699 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
700 } else if (!strcasecmp(tmp->name, "fullname")) {
701 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
702 } else if (!strcasecmp(tmp->name, "context")) {
703 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
704 #ifdef IMAP_STORAGE
705 } else if (!strcasecmp(tmp->name, "imapuser")) {
706 ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
707 } else if (!strcasecmp(tmp->name, "imappassword")) {
708 ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
709 #endif
710 } else
711 apply_option(retval, tmp->name, tmp->value);
712 tmp = tmp->next;
716 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
718 struct ast_variable *var;
719 struct ast_vm_user *retval;
721 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
722 if (!ivm)
723 ast_set_flag(retval, VM_ALLOCED);
724 else
725 memset(retval, 0, sizeof(*retval));
726 if (mailbox)
727 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
728 populate_defaults(retval);
729 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
730 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
731 else
732 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
733 if (var) {
734 apply_options_full(retval, var);
735 ast_variables_destroy(var);
736 } else {
737 if (!ivm)
738 free(retval);
739 retval = NULL;
742 return retval;
745 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
747 /* This function could be made to generate one from a database, too */
748 struct ast_vm_user *vmu=NULL, *cur;
749 AST_LIST_LOCK(&users);
751 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
752 context = "default";
754 AST_LIST_TRAVERSE(&users, cur, list) {
755 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
756 break;
757 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
758 break;
760 if (cur) {
761 /* Make a copy, so that on a reload, we have no race */
762 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
763 memcpy(vmu, cur, sizeof(*vmu));
764 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
765 AST_LIST_NEXT(vmu, list) = NULL;
767 } else
768 vmu = find_user_realtime(ivm, context, mailbox);
769 AST_LIST_UNLOCK(&users);
770 return vmu;
773 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
775 /* This function could be made to generate one from a database, too */
776 struct ast_vm_user *cur;
777 int res = -1;
778 AST_LIST_LOCK(&users);
779 AST_LIST_TRAVERSE(&users, cur, list) {
780 if ((!context || !strcasecmp(context, cur->context)) &&
781 (!strcasecmp(mailbox, cur->mailbox)))
782 break;
784 if (cur) {
785 ast_copy_string(cur->password, newpass, sizeof(cur->password));
786 res = 0;
788 AST_LIST_UNLOCK(&users);
789 return res;
792 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
794 struct ast_config *cfg=NULL;
795 struct ast_variable *var=NULL;
796 struct ast_category *cat=NULL;
797 char *category=NULL, *value=NULL, *new=NULL;
798 const char *tmp=NULL;
800 if (!change_password_realtime(vmu, newpassword))
801 return;
803 /* check voicemail.conf */
804 if ((cfg = ast_config_load_with_comments(VOICEMAIL_CONFIG))) {
805 while ((category = ast_category_browse(cfg, category))) {
806 if (!strcasecmp(category, vmu->context)) {
807 tmp = ast_variable_retrieve(cfg, category, vmu->mailbox);
808 if (!tmp) {
809 ast_log(LOG_WARNING, "We could not find the mailbox.\n");
810 break;
812 value = strstr(tmp,",");
813 if (!value) {
814 ast_log(LOG_WARNING, "variable has bad format.\n");
815 break;
817 new = alloca((strlen(value)+strlen(newpassword)+1));
818 sprintf(new,"%s%s", newpassword, value);
819 if (!(cat = ast_category_get(cfg, category))) {
820 ast_log(LOG_WARNING, "Failed to get category structure.\n");
821 break;
823 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
826 /* save the results */
827 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
828 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
829 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
831 category = NULL;
832 var = NULL;
833 /* check users.conf and update the password stored for the mailbox*/
834 /* if no vmsecret entry exists create one. */
835 if ((cfg = ast_config_load_with_comments("users.conf"))) {
836 if (option_debug > 3)
837 ast_log(LOG_DEBUG, "we are looking for %s\n", vmu->mailbox);
838 while ((category = ast_category_browse(cfg, category))) {
839 if (option_debug > 3)
840 ast_log(LOG_DEBUG, "users.conf: %s\n", category);
841 if (!strcasecmp(category, vmu->mailbox)) {
842 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
843 if (option_debug > 3)
844 ast_log(LOG_DEBUG, "looks like we need to make vmsecret!\n");
845 var = ast_variable_new("vmsecret", newpassword);
847 new = alloca(strlen(newpassword)+1);
848 sprintf(new, "%s", newpassword);
849 if (!(cat = ast_category_get(cfg, category))) {
850 if (option_debug > 3)
851 ast_log(LOG_DEBUG, "failed to get category!\n");
852 break;
854 if (!var)
855 ast_variable_update(cat, "vmsecret", new, NULL, 0);
856 else
857 ast_variable_append(cat, var);
860 /* save the results and clean things up */
861 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
862 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
863 config_text_file_save("users.conf", cfg, "AppVoicemail");
867 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
869 char buf[255];
870 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
871 if (!ast_safe_system(buf)) {
872 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
873 /* Reset the password in memory, too */
874 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
878 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
880 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
883 #ifdef IMAP_STORAGE
884 static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num)
886 if (mkdir(dir, 01777) && (errno != EEXIST)) {
887 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
888 return snprintf(dest, len, "%s/msg%04d", dir, num);
890 return snprintf(dest, len, "%s/msg%04d", dir, num);
893 static void vm_imap_delete(int msgnum, struct vm_state *vms)
895 unsigned long messageNum = 0;
896 char arg[10];
898 /* find real message number based on msgnum */
899 /* this may be an index into vms->msgArray based on the msgnum. */
901 messageNum = vms->msgArray[msgnum];
902 if (messageNum == 0) {
903 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
904 return;
906 if (option_debug > 2)
907 ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
908 /* delete message */
909 snprintf (arg, sizeof(arg), "%lu",messageNum);
910 mail_setflag (vms->mailstream,arg,"\\DELETED");
913 #endif
914 static int make_file(char *dest, int len, char *dir, int num)
916 return snprintf(dest, len, "%s/msg%04d", dir, num);
919 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
920 * \param dest String. base directory.
921 * \param len Length of dest.
922 * \param context String. Ignored if is null or empty string.
923 * \param ext String. Ignored if is null or empty string.
924 * \param folder String. Ignored if is null or empty string.
925 * \return -1 on failure, 0 on success.
927 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
929 mode_t mode = VOICEMAIL_DIR_MODE;
931 if (!ast_strlen_zero(context)) {
932 make_dir(dest, len, context, "", "");
933 if (mkdir(dest, mode) && errno != EEXIST) {
934 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
935 return -1;
938 if (!ast_strlen_zero(ext)) {
939 make_dir(dest, len, context, ext, "");
940 if (mkdir(dest, mode) && errno != EEXIST) {
941 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
942 return -1;
945 if (!ast_strlen_zero(folder)) {
946 make_dir(dest, len, context, ext, folder);
947 if (mkdir(dest, mode) && errno != EEXIST) {
948 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
949 return -1;
952 return 0;
955 /* only return failure if ast_lock_path returns 'timeout',
956 not if the path does not exist or any other reason
958 static int vm_lock_path(const char *path)
960 switch (ast_lock_path(path)) {
961 case AST_LOCK_TIMEOUT:
962 return -1;
963 default:
964 return 0;
969 #ifdef ODBC_STORAGE
970 struct generic_prepare_struct {
971 char *sql;
972 int argc;
973 char **argv;
976 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
978 struct generic_prepare_struct *gps = data;
979 int res, i;
980 SQLHSTMT stmt;
982 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
983 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
984 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
985 return NULL;
987 res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
988 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
989 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
990 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
991 return NULL;
993 for (i = 0; i < gps->argc; i++)
994 SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
996 return stmt;
999 static int retrieve_file(char *dir, int msgnum)
1001 int x = 0;
1002 int res;
1003 int fd=-1;
1004 size_t fdlen = 0;
1005 void *fdm = MAP_FAILED;
1006 SQLSMALLINT colcount=0;
1007 SQLHSTMT stmt;
1008 char sql[PATH_MAX];
1009 char fmt[80]="";
1010 char *c;
1011 char coltitle[256];
1012 SQLSMALLINT collen;
1013 SQLSMALLINT datatype;
1014 SQLSMALLINT decimaldigits;
1015 SQLSMALLINT nullable;
1016 SQLULEN colsize;
1017 SQLLEN colsize2;
1018 FILE *f=NULL;
1019 char rowdata[80];
1020 char fn[PATH_MAX];
1021 char full_fn[PATH_MAX];
1022 char msgnums[80];
1023 char *argv[] = { dir, msgnums };
1024 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1026 struct odbc_obj *obj;
1027 obj = ast_odbc_request_obj(odbc_database, 0);
1028 if (obj) {
1029 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1030 c = strchr(fmt, '|');
1031 if (c)
1032 *c = '\0';
1033 if (!strcasecmp(fmt, "wav49"))
1034 strcpy(fmt, "WAV");
1035 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1036 if (msgnum > -1)
1037 make_file(fn, sizeof(fn), dir, msgnum);
1038 else
1039 ast_copy_string(fn, dir, sizeof(fn));
1040 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1042 if (!(f = fopen(full_fn, "w+"))) {
1043 ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
1044 goto yuck;
1047 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1048 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1049 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1050 if (!stmt) {
1051 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1052 ast_odbc_release_obj(obj);
1053 goto yuck;
1055 res = SQLFetch(stmt);
1056 if (res == SQL_NO_DATA) {
1057 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1058 ast_odbc_release_obj(obj);
1059 goto yuck;
1061 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1062 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1063 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1064 ast_odbc_release_obj(obj);
1065 goto yuck;
1067 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, 0770);
1068 if (fd < 0) {
1069 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1070 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1071 ast_odbc_release_obj(obj);
1072 goto yuck;
1074 res = SQLNumResultCols(stmt, &colcount);
1075 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1076 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1077 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1078 ast_odbc_release_obj(obj);
1079 goto yuck;
1081 if (f)
1082 fprintf(f, "[message]\n");
1083 for (x=0;x<colcount;x++) {
1084 rowdata[0] = '\0';
1085 collen = sizeof(coltitle);
1086 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
1087 &datatype, &colsize, &decimaldigits, &nullable);
1088 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1089 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1090 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1091 ast_odbc_release_obj(obj);
1092 goto yuck;
1094 if (!strcasecmp(coltitle, "recording")) {
1095 off_t offset;
1096 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
1097 fdlen = colsize2;
1098 if (fd > -1) {
1099 char tmp[1]="";
1100 lseek(fd, fdlen - 1, SEEK_SET);
1101 if (write(fd, tmp, 1) != 1) {
1102 close(fd);
1103 fd = -1;
1104 continue;
1106 /* Read out in small chunks */
1107 for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1108 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
1109 ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1110 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1111 ast_odbc_release_obj(obj);
1112 goto yuck;
1113 } else {
1114 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
1115 munmap(fdm, CHUNKSIZE);
1116 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1117 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1118 unlink(full_fn);
1119 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1120 ast_odbc_release_obj(obj);
1121 goto yuck;
1125 truncate(full_fn, fdlen);
1127 } else {
1128 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1129 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1130 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1131 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1132 ast_odbc_release_obj(obj);
1133 goto yuck;
1135 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1136 fprintf(f, "%s=%s\n", coltitle, rowdata);
1139 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1140 ast_odbc_release_obj(obj);
1141 } else
1142 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1143 yuck:
1144 if (f)
1145 fclose(f);
1146 if (fd > -1)
1147 close(fd);
1148 return x - 1;
1151 static int remove_file(char *dir, int msgnum)
1153 char fn[PATH_MAX];
1154 char full_fn[PATH_MAX];
1155 char msgnums[80];
1157 if (msgnum > -1) {
1158 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1159 make_file(fn, sizeof(fn), dir, msgnum);
1160 } else
1161 ast_copy_string(fn, dir, sizeof(fn));
1162 ast_filedelete(fn, NULL);
1163 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1164 unlink(full_fn);
1165 return 0;
1168 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1170 int x = 0;
1171 int res;
1172 SQLHSTMT stmt;
1173 char sql[PATH_MAX];
1174 char rowdata[20];
1175 char *argv[] = { dir };
1176 struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
1178 struct odbc_obj *obj;
1179 obj = ast_odbc_request_obj(odbc_database, 0);
1180 if (obj) {
1181 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1182 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1183 if (!stmt) {
1184 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1185 ast_odbc_release_obj(obj);
1186 goto yuck;
1188 res = SQLFetch(stmt);
1189 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1190 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1191 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1192 ast_odbc_release_obj(obj);
1193 goto yuck;
1195 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1196 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1197 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1198 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1199 ast_odbc_release_obj(obj);
1200 goto yuck;
1202 if (sscanf(rowdata, "%d", &x) != 1)
1203 ast_log(LOG_WARNING, "Failed to read message count!\n");
1204 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1205 ast_odbc_release_obj(obj);
1206 } else
1207 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1208 yuck:
1209 return x - 1;
1212 static int message_exists(char *dir, int msgnum)
1214 int x = 0;
1215 int res;
1216 SQLHSTMT stmt;
1217 char sql[PATH_MAX];
1218 char rowdata[20];
1219 char msgnums[20];
1220 char *argv[] = { dir, msgnums };
1221 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1223 struct odbc_obj *obj;
1224 obj = ast_odbc_request_obj(odbc_database, 0);
1225 if (obj) {
1226 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1227 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1228 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1229 if (!stmt) {
1230 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1231 ast_odbc_release_obj(obj);
1232 goto yuck;
1234 res = SQLFetch(stmt);
1235 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1236 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1237 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1238 ast_odbc_release_obj(obj);
1239 goto yuck;
1241 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1242 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1243 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1244 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1245 ast_odbc_release_obj(obj);
1246 goto yuck;
1248 if (sscanf(rowdata, "%d", &x) != 1)
1249 ast_log(LOG_WARNING, "Failed to read message count!\n");
1250 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1251 ast_odbc_release_obj(obj);
1252 } else
1253 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1254 yuck:
1255 return x;
1258 static int count_messages(struct ast_vm_user *vmu, char *dir)
1260 return last_message_index(vmu, dir) + 1;
1263 static void delete_file(char *sdir, int smsg)
1265 SQLHSTMT stmt;
1266 char sql[PATH_MAX];
1267 char msgnums[20];
1268 char *argv[] = { sdir, msgnums };
1269 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1271 struct odbc_obj *obj;
1272 obj = ast_odbc_request_obj(odbc_database, 0);
1273 if (obj) {
1274 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1275 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1276 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1277 if (!stmt)
1278 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1279 else
1280 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1281 ast_odbc_release_obj(obj);
1282 } else
1283 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1284 return;
1287 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1289 SQLHSTMT stmt;
1290 char sql[512];
1291 char msgnums[20];
1292 char msgnumd[20];
1293 struct odbc_obj *obj;
1294 char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
1295 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1297 delete_file(ddir, dmsg);
1298 obj = ast_odbc_request_obj(odbc_database, 0);
1299 if (obj) {
1300 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1301 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1302 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);
1303 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1304 if (!stmt)
1305 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1306 else
1307 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1308 ast_odbc_release_obj(obj);
1309 } else
1310 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1311 return;
1314 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1316 int x = 0;
1317 int res;
1318 int fd = -1;
1319 void *fdm = MAP_FAILED;
1320 size_t fdlen = -1;
1321 SQLHSTMT stmt;
1322 SQLLEN len;
1323 char sql[PATH_MAX];
1324 char msgnums[20];
1325 char fn[PATH_MAX];
1326 char full_fn[PATH_MAX];
1327 char fmt[80]="";
1328 char *c;
1329 const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1330 const char *category = "";
1331 struct ast_config *cfg=NULL;
1332 struct odbc_obj *obj;
1334 delete_file(dir, msgnum);
1335 obj = ast_odbc_request_obj(odbc_database, 0);
1336 if (obj) {
1337 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1338 c = strchr(fmt, '|');
1339 if (c)
1340 *c = '\0';
1341 if (!strcasecmp(fmt, "wav49"))
1342 strcpy(fmt, "WAV");
1343 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1344 if (msgnum > -1)
1345 make_file(fn, sizeof(fn), dir, msgnum);
1346 else
1347 ast_copy_string(fn, dir, sizeof(fn));
1348 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1349 cfg = ast_config_load(full_fn);
1350 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1351 fd = open(full_fn, O_RDWR);
1352 if (fd < 0) {
1353 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1354 ast_odbc_release_obj(obj);
1355 goto yuck;
1357 if (cfg) {
1358 context = ast_variable_retrieve(cfg, "message", "context");
1359 if (!context) context = "";
1360 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1361 if (!macrocontext) macrocontext = "";
1362 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1363 if (!callerid) callerid = "";
1364 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1365 if (!origtime) origtime = "";
1366 duration = ast_variable_retrieve(cfg, "message", "duration");
1367 if (!duration) duration = "";
1368 category = ast_variable_retrieve(cfg, "message", "category");
1369 if (!category) category = "";
1371 fdlen = lseek(fd, 0, SEEK_END);
1372 lseek(fd, 0, SEEK_SET);
1373 printf("Length is %zd\n", fdlen);
1374 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1375 if (fdm == MAP_FAILED) {
1376 ast_log(LOG_WARNING, "Memory map failed!\n");
1377 ast_odbc_release_obj(obj);
1378 goto yuck;
1380 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1381 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1382 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1383 ast_odbc_release_obj(obj);
1384 goto yuck;
1386 if (!ast_strlen_zero(category))
1387 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1388 else
1389 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1390 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1391 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1392 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1393 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1394 ast_odbc_release_obj(obj);
1395 goto yuck;
1397 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1398 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1399 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1400 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1401 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1402 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1403 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1404 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1405 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1406 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1407 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1408 if (!ast_strlen_zero(category))
1409 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1410 res = ast_odbc_smart_execute(obj, stmt);
1411 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1412 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1413 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1414 ast_odbc_release_obj(obj);
1415 goto yuck;
1417 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1418 ast_odbc_release_obj(obj);
1419 } else
1420 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1421 yuck:
1422 if (cfg)
1423 ast_config_destroy(cfg);
1424 if (fdm != MAP_FAILED)
1425 munmap(fdm, fdlen);
1426 if (fd > -1)
1427 close(fd);
1428 return x;
1431 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1433 SQLHSTMT stmt;
1434 char sql[PATH_MAX];
1435 char msgnums[20];
1436 char msgnumd[20];
1437 struct odbc_obj *obj;
1438 char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
1439 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1441 delete_file(ddir, dmsg);
1442 obj = ast_odbc_request_obj(odbc_database, 0);
1443 if (obj) {
1444 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1445 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1446 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1447 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1448 if (!stmt)
1449 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1450 else
1451 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1452 ast_odbc_release_obj(obj);
1453 } else
1454 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1455 return;
1458 #else
1459 #ifndef IMAP_STORAGE
1460 static int count_messages(struct ast_vm_user *vmu, char *dir)
1462 /* Find all .txt files - even if they are not in sequence from 0000 */
1464 int vmcount = 0;
1465 DIR *vmdir = NULL;
1466 struct dirent *vment = NULL;
1468 if (vm_lock_path(dir))
1469 return ERROR_LOCK_PATH;
1471 if ((vmdir = opendir(dir))) {
1472 while ((vment = readdir(vmdir))) {
1473 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1474 vmcount++;
1476 closedir(vmdir);
1478 ast_unlock_path(dir);
1480 return vmcount;
1483 static void rename_file(char *sfn, char *dfn)
1485 char stxt[PATH_MAX];
1486 char dtxt[PATH_MAX];
1487 ast_filerename(sfn,dfn,NULL);
1488 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1489 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1490 rename(stxt, dtxt);
1492 #endif
1495 * A negative return value indicates an error.
1497 #if (!defined(IMAP_STORAGE) && !defined(ODBC_STORAGE))
1498 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1500 int x;
1501 char fn[PATH_MAX];
1503 if (vm_lock_path(dir))
1504 return ERROR_LOCK_PATH;
1506 for (x = 0; x < vmu->maxmsg; x++) {
1507 make_file(fn, sizeof(fn), dir, x);
1508 if (ast_fileexists(fn, NULL, NULL) < 1)
1509 break;
1511 ast_unlock_path(dir);
1513 return x - 1;
1515 #endif
1516 #endif
1518 static int copy(char *infile, char *outfile)
1520 int ifd;
1521 int ofd;
1522 int res;
1523 int len;
1524 char buf[4096];
1526 #ifdef HARDLINK_WHEN_POSSIBLE
1527 /* Hard link if possible; saves disk space & is faster */
1528 if (link(infile, outfile)) {
1529 #endif
1530 if ((ifd = open(infile, O_RDONLY)) < 0) {
1531 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1532 return -1;
1534 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1535 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1536 close(ifd);
1537 return -1;
1539 do {
1540 len = read(ifd, buf, sizeof(buf));
1541 if (len < 0) {
1542 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1543 close(ifd);
1544 close(ofd);
1545 unlink(outfile);
1547 if (len) {
1548 res = write(ofd, buf, len);
1549 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1550 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1551 close(ifd);
1552 close(ofd);
1553 unlink(outfile);
1556 } while (len);
1557 close(ifd);
1558 close(ofd);
1559 return 0;
1560 #ifdef HARDLINK_WHEN_POSSIBLE
1561 } else {
1562 /* Hard link succeeded */
1563 return 0;
1565 #endif
1568 static void copy_plain_file(char *frompath, char *topath)
1570 char frompath2[PATH_MAX], topath2[PATH_MAX];
1571 ast_filecopy(frompath, topath, NULL);
1572 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1573 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1574 copy(frompath2, topath2);
1577 static int vm_delete(char *file)
1579 char *txt;
1580 int txtsize = 0;
1582 txtsize = (strlen(file) + 5)*sizeof(char);
1583 txt = alloca(txtsize);
1584 /* Sprintf here would safe because we alloca'd exactly the right length,
1585 * but trying to eliminate all sprintf's anyhow
1587 snprintf(txt, txtsize, "%s.txt", file);
1588 unlink(txt);
1589 return ast_filedelete(file, NULL);
1592 static int inbuf(struct baseio *bio, FILE *fi)
1594 int l;
1596 if (bio->ateof)
1597 return 0;
1599 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1600 if (ferror(fi))
1601 return -1;
1603 bio->ateof = 1;
1604 return 0;
1607 bio->iolen= l;
1608 bio->iocp= 0;
1610 return 1;
1613 static int inchar(struct baseio *bio, FILE *fi)
1615 if (bio->iocp>=bio->iolen) {
1616 if (!inbuf(bio, fi))
1617 return EOF;
1620 return bio->iobuf[bio->iocp++];
1623 static int ochar(struct baseio *bio, int c, FILE *so)
1625 if (bio->linelength>=BASELINELEN) {
1626 if (fputs(eol,so)==EOF)
1627 return -1;
1629 bio->linelength= 0;
1632 if (putc(((unsigned char)c),so)==EOF)
1633 return -1;
1635 bio->linelength++;
1637 return 1;
1640 static int base_encode(char *filename, FILE *so)
1642 unsigned char dtable[BASEMAXINLINE];
1643 int i,hiteof= 0;
1644 FILE *fi;
1645 struct baseio bio;
1647 memset(&bio, 0, sizeof(bio));
1648 bio.iocp = BASEMAXINLINE;
1650 if (!(fi = fopen(filename, "rb"))) {
1651 ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
1652 return -1;
1655 for (i= 0;i<9;i++) {
1656 dtable[i]= 'A'+i;
1657 dtable[i+9]= 'J'+i;
1658 dtable[26+i]= 'a'+i;
1659 dtable[26+i+9]= 'j'+i;
1661 for (i= 0;i<8;i++) {
1662 dtable[i+18]= 'S'+i;
1663 dtable[26+i+18]= 's'+i;
1665 for (i= 0;i<10;i++) {
1666 dtable[52+i]= '0'+i;
1668 dtable[62]= '+';
1669 dtable[63]= '/';
1671 while (!hiteof){
1672 unsigned char igroup[3],ogroup[4];
1673 int c,n;
1675 igroup[0]= igroup[1]= igroup[2]= 0;
1677 for (n= 0;n<3;n++) {
1678 if ((c = inchar(&bio, fi)) == EOF) {
1679 hiteof= 1;
1680 break;
1683 igroup[n]= (unsigned char)c;
1686 if (n> 0) {
1687 ogroup[0]= dtable[igroup[0]>>2];
1688 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1689 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1690 ogroup[3]= dtable[igroup[2]&0x3F];
1692 if (n<3) {
1693 ogroup[3]= '=';
1695 if (n<2)
1696 ogroup[2]= '=';
1699 for (i= 0;i<4;i++)
1700 ochar(&bio, ogroup[i], so);
1704 fclose(fi);
1706 if (fputs(eol,so)==EOF)
1707 return 0;
1709 return 1;
1712 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)
1714 char callerid[256];
1715 /* Prepare variables for substition in email body and subject */
1716 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1717 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1718 snprintf(passdata, passdatasize, "%d", msgnum);
1719 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1720 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1721 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1722 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1723 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1724 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1725 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1726 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1729 static char *quote(const char *from, char *to, size_t len)
1731 char *ptr = to;
1732 *ptr++ = '"';
1733 for (; ptr < to + len - 1; from++) {
1734 if (*from == '"')
1735 *ptr++ = '\\';
1736 else if (*from == '\0')
1737 break;
1738 *ptr++ = *from;
1740 if (ptr < to + len - 1)
1741 *ptr++ = '"';
1742 *ptr = '\0';
1743 return to;
1746 * fill in *tm for current time according to the proper timezone, if any.
1747 * Return tm so it can be used as a function argument.
1749 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1751 const struct vm_zone *z = NULL;
1752 time_t t = time(NULL);
1754 /* Does this user have a timezone specified? */
1755 if (!ast_strlen_zero(vmu->zonetag)) {
1756 /* Find the zone in the list */
1757 AST_LIST_LOCK(&zones);
1758 AST_LIST_TRAVERSE(&zones, z, list) {
1759 if (!strcmp(z->name, vmu->zonetag))
1760 break;
1762 AST_LIST_UNLOCK(&zones);
1764 ast_localtime(&t, tm, z ? z->timezone : NULL);
1765 return tm;
1768 /* same as mkstemp, but return a FILE * */
1769 static FILE *vm_mkftemp(char *template)
1771 FILE *p = NULL;
1772 int pfd = mkstemp(template);
1773 chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
1774 if (pfd > -1) {
1775 p = fdopen(pfd, "w+");
1776 if (!p) {
1777 close(pfd);
1778 pfd = -1;
1781 return p;
1784 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)
1786 char date[256];
1787 char host[MAXHOSTNAMELEN] = "";
1788 char who[256];
1789 char bound[256];
1790 char fname[256];
1791 char dur[256];
1792 char tmpcmd[256];
1793 struct tm tm;
1794 char *passdata2;
1795 size_t len_passdata;
1796 #ifdef IMAP_STORAGE
1797 #define ENDL "\r\n"
1798 #else
1799 #define ENDL "\n"
1800 #endif
1802 gethostname(host, sizeof(host) - 1);
1803 if (strchr(srcemail, '@'))
1804 ast_copy_string(who, srcemail, sizeof(who));
1805 else {
1806 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1808 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1809 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1810 fprintf(p, "Date: %s" ENDL, date);
1812 /* Set date format for voicemail mail */
1813 strftime(date, sizeof(date), emaildateformat, &tm);
1815 if (*fromstring) {
1816 struct ast_channel *ast;
1817 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1818 char *passdata;
1819 int vmlen = strlen(fromstring)*3 + 200;
1820 if ((passdata = alloca(vmlen))) {
1821 memset(passdata, 0, vmlen);
1822 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1823 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1824 len_passdata = strlen(passdata) * 2 + 3;
1825 passdata2 = alloca(len_passdata);
1826 fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
1827 } else
1828 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1829 ast_channel_free(ast);
1830 } else
1831 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1832 } else
1833 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
1834 len_passdata = strlen(vmu->fullname) * 2 + 3;
1835 passdata2 = alloca(len_passdata);
1836 fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
1837 if (emailsubject) {
1838 struct ast_channel *ast;
1839 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1840 char *passdata;
1841 int vmlen = strlen(emailsubject)*3 + 200;
1842 if ((passdata = alloca(vmlen))) {
1843 memset(passdata, 0, vmlen);
1844 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1845 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
1846 fprintf(p, "Subject: %s" ENDL, passdata);
1847 } else
1848 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1849 ast_channel_free(ast);
1850 } else
1851 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1852 } else if (*emailtitle) {
1853 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1854 fprintf(p, ENDL) ;
1855 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1856 fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
1857 else
1858 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
1859 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
1860 if (imap) {
1861 /* additional information needed for IMAP searching */
1862 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
1863 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
1864 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
1865 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
1866 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
1867 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
1868 fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
1869 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
1870 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
1871 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
1872 if (!ast_strlen_zero(category))
1873 fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
1874 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
1875 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
1877 if (!ast_strlen_zero(cidnum))
1878 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, cidnum);
1879 if (!ast_strlen_zero(cidname))
1880 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, cidname);
1881 fprintf(p, "MIME-Version: 1.0" ENDL);
1882 if (attach_user_voicemail) {
1883 /* Something unique. */
1884 snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
1886 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
1887 fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
1888 fprintf(p, "--%s" ENDL, bound);
1890 fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
1891 if (emailbody) {
1892 struct ast_channel *ast;
1893 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1894 char *passdata;
1895 int vmlen = strlen(emailbody)*3 + 200;
1896 if ((passdata = alloca(vmlen))) {
1897 memset(passdata, 0, vmlen);
1898 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1899 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
1900 fprintf(p, "%s" ENDL, passdata);
1901 } else
1902 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1903 ast_channel_free(ast);
1904 } else
1905 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1906 } else {
1907 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
1909 "in mailbox %s from %s, on %s so you might" ENDL
1910 "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname,
1911 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1913 if (attach_user_voicemail) {
1914 /* Eww. We want formats to tell us their own MIME type */
1915 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
1916 char tmpdir[256], newtmp[256];
1917 int tmpfd = -1;
1919 if (vmu->volgain < -.001 || vmu->volgain > .001) {
1920 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
1921 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
1922 tmpfd = mkstemp(newtmp);
1923 chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
1924 if (option_debug > 2)
1925 ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
1926 if (tmpfd > -1) {
1927 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
1928 ast_safe_system(tmpcmd);
1929 attach = newtmp;
1930 if (option_debug > 2)
1931 ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
1934 fprintf(p, "--%s" ENDL, bound);
1935 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
1936 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
1937 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
1938 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
1939 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1940 base_encode(fname, p);
1941 fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
1942 if (tmpfd > -1) {
1943 unlink(fname);
1944 close(tmpfd);
1945 unlink(newtmp);
1948 #undef ENDL
1950 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)
1952 FILE *p=NULL;
1953 char tmp[80] = "/tmp/astmail-XXXXXX";
1954 char tmp2[256];
1956 if (vmu && ast_strlen_zero(vmu->email)) {
1957 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
1958 return(0);
1960 if (!strcmp(format, "wav49"))
1961 format = "WAV";
1962 if (option_debug > 2)
1963 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));
1964 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1965 command hangs */
1966 if ((p = vm_mkftemp(tmp)) == NULL) {
1967 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1968 return -1;
1969 } else {
1970 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
1971 fclose(p);
1972 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1973 ast_safe_system(tmp2);
1974 if (option_debug > 2)
1975 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
1977 return 0;
1980 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)
1982 char date[256];
1983 char host[MAXHOSTNAMELEN] = "";
1984 char who[256];
1985 char dur[PATH_MAX];
1986 char tmp[80] = "/tmp/astmail-XXXXXX";
1987 char tmp2[PATH_MAX];
1988 struct tm tm;
1989 FILE *p;
1991 if ((p = vm_mkftemp(tmp)) == NULL) {
1992 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1993 return -1;
1994 } else {
1995 gethostname(host, sizeof(host)-1);
1996 if (strchr(srcemail, '@'))
1997 ast_copy_string(who, srcemail, sizeof(who));
1998 else {
1999 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2001 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2002 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2003 fprintf(p, "Date: %s\n", date);
2005 if (*pagerfromstring) {
2006 struct ast_channel *ast;
2007 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2008 char *passdata;
2009 int vmlen = strlen(fromstring)*3 + 200;
2010 if ((passdata = alloca(vmlen))) {
2011 memset(passdata, 0, vmlen);
2012 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2013 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2014 fprintf(p, "From: %s <%s>\n", passdata, who);
2015 } else
2016 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2017 ast_channel_free(ast);
2018 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2019 } else
2020 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2021 fprintf(p, "To: %s\n", pager);
2022 if (pagersubject) {
2023 struct ast_channel *ast;
2024 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2025 char *passdata;
2026 int vmlen = strlen(pagersubject) * 3 + 200;
2027 if ((passdata = alloca(vmlen))) {
2028 memset(passdata, 0, vmlen);
2029 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2030 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2031 fprintf(p, "Subject: %s\n\n", passdata);
2032 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2033 ast_channel_free(ast);
2034 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2035 } else
2036 fprintf(p, "Subject: New VM\n\n");
2037 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2038 if (pagerbody) {
2039 struct ast_channel *ast;
2040 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2041 char *passdata;
2042 int vmlen = strlen(pagerbody)*3 + 200;
2043 if ((passdata = alloca(vmlen))) {
2044 memset(passdata, 0, vmlen);
2045 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2046 pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2047 fprintf(p, "%s\n", passdata);
2048 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2049 ast_channel_free(ast);
2050 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2051 } else {
2052 fprintf(p, "New %s long msg in box %s\n"
2053 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2055 fclose(p);
2056 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2057 ast_safe_system(tmp2);
2058 if (option_debug > 2)
2059 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
2061 return 0;
2064 static int get_date(char *s, int len)
2066 struct tm tm;
2067 time_t t;
2069 time(&t);
2071 ast_localtime(&t, &tm, NULL);
2073 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2076 static int play_greeting(struct ast_channel *chan, struct ast_vm_user *vmu, char *filename, char *ecodes)
2078 int res = -2;
2080 #ifdef ODBC_STORAGE
2081 int success =
2082 #endif
2083 RETRIEVE(filename, -1);
2084 if (ast_fileexists(filename, NULL, NULL) > 0) {
2085 res = ast_streamfile(chan, filename, chan->language);
2086 if (res > -1)
2087 res = ast_waitstream(chan, ecodes);
2088 #ifdef ODBC_STORAGE
2089 if (success == -1) {
2090 /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
2091 if (option_debug)
2092 ast_log(LOG_DEBUG, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
2093 store_file(filename, vmu->mailbox, vmu->context, -1);
2095 #endif
2097 DISPOSE(filename, -1);
2099 return res;
2102 static int invent_message(struct ast_channel *chan, struct ast_vm_user *vmu, char *ext, int busy, char *ecodes)
2104 int res;
2105 char fn[PATH_MAX];
2106 char dest[PATH_MAX];
2108 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, ext);
2110 if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "greet"))) {
2111 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
2112 return -1;
2115 res = play_greeting(chan, vmu, fn, ecodes);
2116 if (res == -2) {
2117 /* File did not exist */
2118 res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
2119 if (res)
2120 return res;
2121 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2124 if (res)
2125 return res;
2127 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
2128 return res;
2131 static void free_user(struct ast_vm_user *vmu)
2133 if (ast_test_flag(vmu, VM_ALLOCED))
2134 free(vmu);
2137 static void free_zone(struct vm_zone *z)
2139 free(z);
2142 static const char *mbox(int id)
2144 static const char *msgs[] = {
2145 "INBOX",
2146 "Old",
2147 "Work",
2148 "Family",
2149 "Friends",
2150 "Cust1",
2151 "Cust2",
2152 "Cust3",
2153 "Cust4",
2154 "Cust5",
2156 return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "tmp";
2158 #ifdef IMAP_STORAGE
2159 static int folder_int(const char *folder)
2161 /*assume a NULL folder means INBOX*/
2162 if (!folder)
2163 return 0;
2164 if (!strcasecmp(folder, "INBOX"))
2165 return 0;
2166 else if (!strcasecmp(folder, "Old"))
2167 return 1;
2168 else if (!strcasecmp(folder, "Work"))
2169 return 2;
2170 else if (!strcasecmp(folder, "Family"))
2171 return 3;
2172 else if (!strcasecmp(folder, "Friends"))
2173 return 4;
2174 else if (!strcasecmp(folder, "Cust1"))
2175 return 5;
2176 else if (!strcasecmp(folder, "Cust2"))
2177 return 6;
2178 else if (!strcasecmp(folder, "Cust3"))
2179 return 7;
2180 else if (!strcasecmp(folder, "Cust4"))
2181 return 8;
2182 else if (!strcasecmp(folder, "Cust5"))
2183 return 9;
2184 else /*assume they meant INBOX if folder is not found otherwise*/
2185 return 0;
2187 #endif
2189 #ifdef ODBC_STORAGE
2190 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
2191 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2193 int x = -1;
2194 int res;
2195 SQLHSTMT stmt;
2196 char sql[PATH_MAX];
2197 char rowdata[20];
2198 char tmp[PATH_MAX] = "";
2199 struct odbc_obj *obj;
2200 char *context;
2201 struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2203 if (newmsgs)
2204 *newmsgs = 0;
2205 if (oldmsgs)
2206 *oldmsgs = 0;
2208 /* If no mailbox, return immediately */
2209 if (ast_strlen_zero(mailbox))
2210 return 0;
2212 ast_copy_string(tmp, mailbox, sizeof(tmp));
2214 context = strchr(tmp, '@');
2215 if (context) {
2216 *context = '\0';
2217 context++;
2218 } else
2219 context = "default";
2221 obj = ast_odbc_request_obj(odbc_database, 0);
2222 if (obj) {
2223 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2224 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2225 if (!stmt) {
2226 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2227 ast_odbc_release_obj(obj);
2228 goto yuck;
2230 res = SQLFetch(stmt);
2231 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2232 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2233 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2234 ast_odbc_release_obj(obj);
2235 goto yuck;
2237 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2238 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2239 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2240 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2241 ast_odbc_release_obj(obj);
2242 goto yuck;
2244 *newmsgs = atoi(rowdata);
2245 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2247 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2248 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2249 if (!stmt) {
2250 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2251 ast_odbc_release_obj(obj);
2252 goto yuck;
2254 res = SQLFetch(stmt);
2255 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2256 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2257 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2258 ast_odbc_release_obj(obj);
2259 goto yuck;
2261 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2262 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2263 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2264 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2265 ast_odbc_release_obj(obj);
2266 goto yuck;
2268 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2269 ast_odbc_release_obj(obj);
2270 *oldmsgs = atoi(rowdata);
2271 x = 0;
2272 } else
2273 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2275 yuck:
2276 return x;
2279 static int messagecount(const char *context, const char *mailbox, const char *folder)
2281 struct odbc_obj *obj = NULL;
2282 int nummsgs = 0;
2283 int res;
2284 SQLHSTMT stmt = NULL;
2285 char sql[PATH_MAX];
2286 char rowdata[20];
2287 struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2288 if (!folder)
2289 folder = "INBOX";
2290 /* If no mailbox, return immediately */
2291 if (ast_strlen_zero(mailbox))
2292 return 0;
2294 obj = ast_odbc_request_obj(odbc_database, 0);
2295 if (obj) {
2296 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2297 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2298 if (!stmt) {
2299 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2300 goto yuck;
2302 res = SQLFetch(stmt);
2303 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2304 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2305 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2306 goto yuck;
2308 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2309 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2310 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2311 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2312 goto yuck;
2314 nummsgs = atoi(rowdata);
2315 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2316 } else
2317 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2319 yuck:
2320 if (obj)
2321 ast_odbc_release_obj(obj);
2322 return nummsgs;
2325 static int has_voicemail(const char *mailbox, const char *folder)
2327 char tmp[256], *tmp2 = tmp, *mbox, *context;
2328 ast_copy_string(tmp, mailbox, sizeof(tmp));
2329 while ((context = mbox = strsep(&tmp2, ","))) {
2330 strsep(&context, "@");
2331 if (ast_strlen_zero(context))
2332 context = "default";
2333 if (messagecount(context, mbox, folder))
2334 return 1;
2336 return 0;
2339 #elif defined(IMAP_STORAGE)
2341 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)
2343 char *myserveremail = serveremail;
2344 char fn[PATH_MAX];
2345 char mailbox[256];
2346 char *stringp;
2347 FILE *p=NULL;
2348 char tmp[80] = "/tmp/astmail-XXXXXX";
2349 long len;
2350 void *buf;
2351 int tempcopy = 0;
2352 STRING str;
2354 /*Greetings are not retrieved from IMAP, so there is no reason to attempt storing them there either*/
2355 if (msgnum < 0)
2356 return 0;
2358 /* Attach only the first format */
2359 fmt = ast_strdupa(fmt);
2360 stringp = fmt;
2361 strsep(&stringp, "|");
2363 if (!ast_strlen_zero(vmu->serveremail))
2364 myserveremail = vmu->serveremail;
2366 make_file(fn, sizeof(fn), dir, msgnum);
2368 if (ast_strlen_zero(vmu->email)) {
2369 /*we need the vmu->email to be set when we call make_email_file, but if we keep it set,
2370 * a duplicate e-mail will be created. So at the end of this function, we will revert back to an empty
2371 * string if tempcopy is 1
2373 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2374 tempcopy = 1;
2377 if (!strcmp(fmt, "wav49"))
2378 fmt = "WAV";
2379 if (option_debug > 2)
2380 ast_log(LOG_DEBUG, "Storing file '%s', format '%s'\n", fn, fmt);
2381 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2382 command hangs */
2383 if ((p = vm_mkftemp(tmp)) == NULL) {
2384 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2385 if (tempcopy)
2386 *(vmu->email) = '\0';
2387 return -1;
2388 } else {
2389 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);
2390 /* read mail file to memory */
2391 len = ftell(p);
2392 rewind(p);
2393 if ((buf = ast_malloc(len+1)) == NIL) {
2394 ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2395 fclose(p);
2396 return -1;
2398 fread(buf, len, 1, p);
2399 ((char *)buf)[len] = '\0';
2400 INIT(&str, mail_string, buf, len);
2401 init_mailstream(vms, 0);
2402 imap_mailbox_name(mailbox, sizeof(mailbox), vms, 0, 1);
2403 if (!mail_append(vms->mailstream, mailbox, &str))
2404 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2405 fclose(p);
2406 unlink(tmp);
2407 ast_free(buf);
2408 if (option_debug > 2)
2409 ast_log(LOG_DEBUG, "%s stored\n", fn);
2411 if (tempcopy)
2412 *(vmu->email) = '\0';
2413 return 0;
2417 static int messagecount(const char *context, const char *mailbox, const char *folder)
2419 SEARCHPGM *pgm;
2420 SEARCHHEADER *hdr;
2422 struct ast_vm_user *vmu, vmus;
2423 struct vm_state *vms_p;
2424 int ret = 0;
2425 int fold = folder_int(folder);
2427 if (ast_strlen_zero(mailbox))
2428 return 0;
2430 /* We have to get the user before we can open the stream! */
2431 /* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2432 vmu = find_user(&vmus, context, mailbox);
2433 if (!vmu) {
2434 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailbox,context);
2435 return -1;
2436 } else {
2437 /* No IMAP account available */
2438 if (vmu->imapuser[0] == '\0') {
2439 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2440 return -1;
2444 /* check if someone is accessing this box right now... */
2445 vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
2446 if (!vms_p) {
2447 vms_p = get_vm_state_by_mailbox(mailbox,1);
2449 if (vms_p) {
2450 if (option_debug > 2)
2451 ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
2452 if (fold == 0) {/*INBOX*/
2453 return vms_p->newmessages;
2455 if (fold == 1) {/*Old messages*/
2456 return vms_p->oldmessages;
2460 /* add one if not there... */
2461 vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
2462 if (!vms_p) {
2463 vms_p = get_vm_state_by_mailbox(mailbox,0);
2466 if (!vms_p) {
2467 if (option_debug > 2)
2468 ast_log (LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2469 if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
2470 return -1;
2472 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2473 ast_copy_string(vms_p->username, mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
2474 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2475 if (option_debug > 2)
2476 ast_log (LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2477 vms_p->updated = 1;
2478 /* set mailbox to INBOX! */
2479 ast_copy_string(vms_p->curbox, mbox(fold), sizeof(vms_p->curbox));
2480 init_vm_state(vms_p);
2481 vmstate_insert(vms_p);
2483 ret = init_mailstream(vms_p, fold);
2484 if (!vms_p->mailstream) {
2485 ast_log (LOG_ERROR,"IMAP mailstream is NULL\n");
2486 return -1;
2488 if (ret == 0) {
2489 pgm = mail_newsearchpgm ();
2490 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
2491 pgm->header = hdr;
2492 if (fold != 1) {
2493 pgm->unseen = 1;
2494 pgm->seen = 0;
2496 /* In the special case where fold is 1 (old messages) we have to do things a bit
2497 * differently. Old messages are stored in the INBOX but are marked as "seen"
2499 else {
2500 pgm->unseen = 0;
2501 pgm->seen = 1;
2503 pgm->undeleted = 1;
2504 pgm->deleted = 0;
2506 vms_p->vmArrayIndex = 0;
2507 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2508 if (fold == 0)
2509 vms_p->newmessages = vms_p->vmArrayIndex;
2510 if (fold == 1)
2511 vms_p->oldmessages = vms_p->vmArrayIndex;
2512 /*Freeing the searchpgm also frees the searchhdr*/
2513 mail_free_searchpgm(&pgm);
2514 vms_p->updated = 0;
2515 return vms_p->vmArrayIndex;
2516 } else {
2517 mail_ping(vms_p->mailstream);
2519 return 0;
2521 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
2523 char tmp[PATH_MAX] = "";
2524 char *mailboxnc;
2525 char *context;
2526 char *mb;
2527 char *cur;
2528 if (newmsgs)
2529 *newmsgs = 0;
2530 if (oldmsgs)
2531 *oldmsgs = 0;
2533 if (option_debug > 2)
2534 ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox_context);
2535 /* If no mailbox, return immediately */
2536 if (ast_strlen_zero(mailbox_context))
2537 return 0;
2539 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2540 context = strchr(tmp, '@');
2541 if (strchr(mailbox_context, ',')) {
2542 int tmpnew, tmpold;
2543 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2544 mb = tmp;
2545 while ((cur = strsep(&mb, ", "))) {
2546 if (!ast_strlen_zero(cur)) {
2547 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2548 return -1;
2549 else {
2550 if (newmsgs)
2551 *newmsgs += tmpnew;
2552 if (oldmsgs)
2553 *oldmsgs += tmpold;
2557 return 0;
2559 if (context) {
2560 *context = '\0';
2561 mailboxnc = tmp;
2562 context++;
2563 } else {
2564 context = "default";
2565 mailboxnc = (char *)mailbox_context;
2567 if (newmsgs) {
2568 if ((*newmsgs = messagecount(context, mailboxnc, "INBOX")) < 0)
2569 return -1;
2571 if (oldmsgs) {
2572 if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
2573 return -1;
2575 return 0;
2579 static int has_voicemail(const char *mailbox, const char *folder)
2581 char tmp[256], *tmp2, *mbox, *context;
2582 ast_copy_string(tmp, mailbox, sizeof(tmp));
2583 tmp2 = tmp;
2584 if (strchr(tmp2, ',')) {
2585 while ((mbox = strsep(&tmp2, ","))) {
2586 if (!ast_strlen_zero(mbox)) {
2587 if (has_voicemail(mbox, folder))
2588 return 1;
2592 if ((context= strchr(tmp, '@')))
2593 *context++ = '\0';
2594 else
2595 context = "default";
2596 return messagecount(context, tmp, folder) ? 1 : 0;
2599 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)
2601 struct vm_state *sendvms = NULL, *destvms = NULL;
2602 char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2603 if (msgnum >= recip->maxmsg) {
2604 ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
2605 return -1;
2607 if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
2608 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
2609 return -1;
2611 if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
2612 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
2613 return -1;
2615 snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
2616 if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T))
2617 return 0;
2618 ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
2619 return -1;
2622 #endif
2623 #ifndef IMAP_STORAGE
2624 /* copy message only used by file storage */
2625 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)
2627 char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
2628 const char *frombox = mbox(imbox);
2629 int recipmsgnum;
2631 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2633 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2635 if (!dir)
2636 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2637 else
2638 ast_copy_string(fromdir, dir, sizeof(fromdir));
2640 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2642 if (vm_lock_path(todir))
2643 return ERROR_LOCK_PATH;
2645 recipmsgnum = 0;
2646 do {
2647 make_file(topath, sizeof(topath), todir, recipmsgnum);
2648 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
2649 break;
2650 recipmsgnum++;
2651 } while (recipmsgnum < recip->maxmsg);
2652 if (recipmsgnum < recip->maxmsg) {
2653 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2654 } else {
2655 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2657 ast_unlock_path(todir);
2658 notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2660 return 0;
2662 #endif
2663 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2664 static int messagecount(const char *context, const char *mailbox, const char *folder)
2666 return __has_voicemail(context, mailbox, folder, 0);
2670 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2672 DIR *dir;
2673 struct dirent *de;
2674 char fn[256];
2675 int ret = 0;
2676 if (!folder)
2677 folder = "INBOX";
2678 /* If no mailbox, return immediately */
2679 if (ast_strlen_zero(mailbox))
2680 return 0;
2681 if (!context)
2682 context = "default";
2683 snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2684 dir = opendir(fn);
2685 if (!dir)
2686 return 0;
2687 while ((de = readdir(dir))) {
2688 if (!strncasecmp(de->d_name, "msg", 3)) {
2689 if (shortcircuit) {
2690 ret = 1;
2691 break;
2692 } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2693 ret++;
2696 closedir(dir);
2697 return ret;
2701 static int has_voicemail(const char *mailbox, const char *folder)
2703 char tmp[256], *tmp2 = tmp, *mbox, *context;
2704 ast_copy_string(tmp, mailbox, sizeof(tmp));
2705 while ((mbox = strsep(&tmp2, ","))) {
2706 if ((context = strchr(mbox, '@')))
2707 *context++ = '\0';
2708 else
2709 context = "default";
2710 if (__has_voicemail(context, mbox, folder, 1))
2711 return 1;
2713 return 0;
2717 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2719 char tmp[256];
2720 char *context;
2722 if (newmsgs)
2723 *newmsgs = 0;
2724 if (oldmsgs)
2725 *oldmsgs = 0;
2726 /* If no mailbox, return immediately */
2727 if (ast_strlen_zero(mailbox))
2728 return 0;
2729 if (strchr(mailbox, ',')) {
2730 int tmpnew, tmpold;
2731 char *mb, *cur;
2733 ast_copy_string(tmp, mailbox, sizeof(tmp));
2734 mb = tmp;
2735 while ((cur = strsep(&mb, ", "))) {
2736 if (!ast_strlen_zero(cur)) {
2737 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2738 return -1;
2739 else {
2740 if (newmsgs)
2741 *newmsgs += tmpnew;
2742 if (oldmsgs)
2743 *oldmsgs += tmpold;
2747 return 0;
2749 ast_copy_string(tmp, mailbox, sizeof(tmp));
2750 context = strchr(tmp, '@');
2751 if (context) {
2752 *context = '\0';
2753 context++;
2754 } else
2755 context = "default";
2756 if (newmsgs)
2757 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2758 if (oldmsgs)
2759 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2760 return 0;
2763 #endif
2765 static void run_externnotify(char *context, char *extension)
2767 char arguments[255];
2768 char ext_context[256] = "";
2769 int newvoicemails = 0, oldvoicemails = 0;
2770 struct ast_smdi_mwi_message *mwi_msg;
2772 if (!ast_strlen_zero(context))
2773 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2774 else
2775 ast_copy_string(ext_context, extension, sizeof(ext_context));
2777 if (!strcasecmp(externnotify, "smdi")) {
2778 if (ast_app_has_voicemail(ext_context, NULL))
2779 ast_smdi_mwi_set(smdi_iface, extension);
2780 else
2781 ast_smdi_mwi_unset(smdi_iface, extension);
2783 if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
2784 ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
2785 if (!strncmp(mwi_msg->cause, "INV", 3))
2786 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2787 else if (!strncmp(mwi_msg->cause, "BLK", 3))
2788 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2789 ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2790 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2791 } else {
2792 if (option_debug)
2793 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s\n", extension);
2795 } else if (!ast_strlen_zero(externnotify)) {
2796 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2797 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2798 } else {
2799 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2800 if (option_debug)
2801 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2802 ast_safe_system(arguments);
2807 struct leave_vm_options {
2808 unsigned int flags;
2809 signed char record_gain;
2812 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2814 #ifdef IMAP_STORAGE
2815 int newmsgs, oldmsgs;
2816 struct vm_state *vms = NULL;
2817 #endif
2818 char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
2819 char callerid[256];
2820 FILE *txt;
2821 char date[256];
2822 int txtdes;
2823 int res = 0;
2824 int msgnum;
2825 int duration = 0;
2826 int ausemacro = 0;
2827 int ousemacro = 0;
2828 int ouseexten = 0;
2829 char dir[PATH_MAX], tmpdir[PATH_MAX];
2830 char dest[PATH_MAX];
2831 char fn[PATH_MAX];
2832 char prefile[PATH_MAX] = "";
2833 char tempfile[PATH_MAX] = "";
2834 char ext_context[256] = "";
2835 char fmt[80];
2836 char *context;
2837 char ecodes[16] = "#";
2838 char tmp[1024] = "", *tmpptr;
2839 struct ast_vm_user *vmu;
2840 struct ast_vm_user svm;
2841 const char *category = NULL;
2843 ast_copy_string(tmp, ext, sizeof(tmp));
2844 ext = tmp;
2845 context = strchr(tmp, '@');
2846 if (context) {
2847 *context++ = '\0';
2848 tmpptr = strchr(context, '&');
2849 } else {
2850 tmpptr = strchr(ext, '&');
2853 if (tmpptr)
2854 *tmpptr++ = '\0';
2856 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2858 if (option_debug > 2)
2859 ast_log(LOG_DEBUG, "Before find_user\n");
2860 if (!(vmu = find_user(&svm, context, ext))) {
2861 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2862 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2863 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2864 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2865 return res;
2867 /* Setup pre-file if appropriate */
2868 if (strcmp(vmu->context, "default"))
2869 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2870 else
2871 ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
2872 if (ast_test_flag(options, OPT_BUSY_GREETING)) {
2873 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
2874 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2875 } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
2876 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
2877 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2879 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2880 if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
2881 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
2882 return -1;
2884 RETRIEVE(tempfile, -1);
2885 if (ast_fileexists(tempfile, NULL, NULL) > 0)
2886 ast_copy_string(prefile, tempfile, sizeof(prefile));
2887 DISPOSE(tempfile, -1);
2888 /* It's easier just to try to make it than to check for its existence */
2889 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2890 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
2892 /* Check current or macro-calling context for special extensions */
2893 if (ast_test_flag(vmu, VM_OPERATOR)) {
2894 if (!ast_strlen_zero(vmu->exit)) {
2895 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
2896 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2897 ouseexten = 1;
2899 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
2900 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2901 ouseexten = 1;
2903 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2904 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2905 ousemacro = 1;
2909 if (!ast_strlen_zero(vmu->exit)) {
2910 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2911 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2912 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2913 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2914 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2915 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2916 ausemacro = 1;
2919 /* Play the beginning intro if desired */
2920 if (!ast_strlen_zero(prefile)) {
2921 res = play_greeting(chan, vmu, prefile, ecodes);
2922 if (res == -2) {
2923 /* The file did not exist */
2924 if (option_debug)
2925 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2926 res = invent_message(chan, vmu, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2928 if (res < 0) {
2929 if (option_debug)
2930 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2931 free_user(vmu);
2932 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2933 return -1;
2936 if (res == '#') {
2937 /* On a '#' we skip the instructions */
2938 ast_set_flag(options, OPT_SILENT);
2939 res = 0;
2941 if (!res && !ast_test_flag(options, OPT_SILENT)) {
2942 res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
2943 if (res == '#') {
2944 ast_set_flag(options, OPT_SILENT);
2945 res = 0;
2948 if (res > 0)
2949 ast_stopstream(chan);
2950 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2951 other than the operator -- an automated attendant or mailbox login for example */
2952 if (res == '*') {
2953 chan->exten[0] = 'a';
2954 chan->exten[1] = '\0';
2955 if (!ast_strlen_zero(vmu->exit)) {
2956 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2957 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2958 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2960 chan->priority = 0;
2961 free_user(vmu);
2962 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2963 return 0;
2966 /* Check for a '0' here */
2967 if (res == '0') {
2968 transfer:
2969 if (ouseexten || ousemacro) {
2970 chan->exten[0] = 'o';
2971 chan->exten[1] = '\0';
2972 if (!ast_strlen_zero(vmu->exit)) {
2973 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2974 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2975 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2977 ast_play_and_wait(chan, "transfer");
2978 chan->priority = 0;
2979 free_user(vmu);
2980 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2982 return 0;
2984 if (res < 0) {
2985 free_user(vmu);
2986 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2987 return -1;
2989 /* The meat of recording the message... All the announcements and beeps have been played*/
2990 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2991 if (!ast_strlen_zero(fmt)) {
2992 msgnum = 0;
2994 #ifdef IMAP_STORAGE
2995 /* Is ext a mailbox? */
2996 /* must open stream for this user to get info! */
2997 res = inboxcount(ext_context, &newmsgs, &oldmsgs);
2998 if (res < 0) {
2999 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
3000 return -1;
3002 if (!(vms = get_vm_state_by_mailbox(ext,0))) {
3003 /*It is possible under certain circumstances that inboxcount did not create a vm_state when it was needed. This is a catchall which will
3004 * rarely be used*/
3005 if (!(vms = ast_calloc(1, sizeof(*vms)))) {
3006 ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
3007 return -1;
3009 ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
3010 ast_copy_string(vms->username, ext, sizeof(vms->username));
3011 vms->mailstream = NIL;
3012 if (option_debug > 2)
3013 ast_log(LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms->imapuser);
3014 vms->updated=1;
3015 ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
3016 init_vm_state(vms);
3017 vmstate_insert(vms);
3018 vms = get_vm_state_by_mailbox(ext,0);
3020 vms->newmessages++;
3021 /* here is a big difference! We add one to it later */
3022 msgnum = newmsgs + oldmsgs;
3023 if (option_debug > 2)
3024 ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3025 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
3026 /* set variable for compatability */
3027 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3029 /* Check if mailbox is full */
3030 check_quota(vms, imapfolder);
3031 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
3032 if (option_debug)
3033 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
3034 ast_play_and_wait(chan, "vm-mailboxfull");
3035 return -1;
3037 if (option_debug > 2)
3038 ast_log(LOG_DEBUG, "Checking message number quota - mailbox has %d messages, maximum is set to %d\n",msgnum,vmu->maxmsg);
3039 if (msgnum >= vmu->maxmsg) {
3040 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3041 if (!res)
3042 res = ast_waitstream(chan, "");
3043 ast_log(LOG_WARNING, "No more messages possible\n");
3044 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3045 goto leave_vm_out;
3048 /* Check if we have exceeded maxmsg */
3049 if (msgnum >= vmu->maxmsg) {
3050 ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u > %u)\n", msgnum, vmu->maxmsg);
3051 ast_play_and_wait(chan, "vm-mailboxfull");
3052 return -1;
3054 /* here is a big difference! We add one to it later */
3055 if (option_debug > 2)
3056 ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3057 #else
3058 if (count_messages(vmu, dir) >= vmu->maxmsg) {
3059 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3060 if (!res)
3061 res = ast_waitstream(chan, "");
3062 ast_log(LOG_WARNING, "No more messages possible\n");
3063 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3064 goto leave_vm_out;
3067 #endif
3068 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
3069 txtdes = mkstemp(tmptxtfile);
3070 chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
3071 if (txtdes < 0) {
3072 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3073 if (!res)
3074 res = ast_waitstream(chan, "");
3075 ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
3076 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3077 goto leave_vm_out;
3080 /* Now play the beep once we have the message number for our next message. */
3081 if (res >= 0) {
3082 /* Unless we're *really* silent, try to send the beep */
3083 res = ast_stream_and_wait(chan, "beep", chan->language, "");
3086 /* Store information */
3087 txt = fdopen(txtdes, "w+");
3088 if (txt) {
3089 get_date(date, sizeof(date));
3090 fprintf(txt,
3091 ";\n"
3092 "; Message Information file\n"
3093 ";\n"
3094 "[message]\n"
3095 "origmailbox=%s\n"
3096 "context=%s\n"
3097 "macrocontext=%s\n"
3098 "exten=%s\n"
3099 "priority=%d\n"
3100 "callerchan=%s\n"
3101 "callerid=%s\n"
3102 "origdate=%s\n"
3103 "origtime=%ld\n"
3104 "category=%s\n",
3105 ext,
3106 chan->context,
3107 chan->macrocontext,
3108 chan->exten,
3109 chan->priority,
3110 chan->name,
3111 ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
3112 date, (long)time(NULL),
3113 category ? category : "");
3114 } else
3115 ast_log(LOG_WARNING, "Error opening text file for output\n");
3116 #ifdef IMAP_STORAGE
3117 res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
3118 #else
3119 res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL);
3120 #endif
3122 if (txt) {
3123 if (duration < vmminmessage) {
3124 fclose(txt);
3125 if (option_verbose > 2)
3126 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
3127 ast_filedelete(tmptxtfile, NULL);
3128 unlink(tmptxtfile);
3129 } else {
3130 fprintf(txt, "duration=%d\n", duration);
3131 fclose(txt);
3132 if (vm_lock_path(dir)) {
3133 ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
3134 /* Delete files */
3135 ast_filedelete(tmptxtfile, NULL);
3136 unlink(tmptxtfile);
3137 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
3138 if (option_debug)
3139 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
3140 unlink(tmptxtfile);
3141 ast_unlock_path(dir);
3142 } else {
3143 for (;;) {
3144 make_file(fn, sizeof(fn), dir, msgnum);
3145 if (!EXISTS(dir, msgnum, fn, NULL))
3146 break;
3147 msgnum++;
3150 /* assign a variable with the name of the voicemail file */
3151 #ifndef IMAP_STORAGE
3152 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3153 #else
3154 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3155 #endif
3157 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3158 ast_filerename(tmptxtfile, fn, NULL);
3159 rename(tmptxtfile, txtfile);
3161 ast_unlock_path(dir);
3162 /* We must store the file first, before copying the message, because
3163 * ODBC storage does the entire copy with SQL.
3165 if (ast_fileexists(fn, NULL, NULL) > 0) {
3166 STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
3169 /* Are there to be more recipients of this message? */
3170 while (tmpptr) {
3171 struct ast_vm_user recipu, *recip;
3172 char *exten, *context;
3174 exten = strsep(&tmpptr, "&");
3175 context = strchr(exten, '@');
3176 if (context) {
3177 *context = '\0';
3178 context++;
3180 if ((recip = find_user(&recipu, context, exten))) {
3181 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
3182 free_user(recip);
3185 /* Notification and disposal needs to happen after the copy, though. */
3186 if (ast_fileexists(fn, NULL, NULL)) {
3187 notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
3188 DISPOSE(dir, msgnum);
3193 if (res == '0') {
3194 goto transfer;
3195 } else if (res > 0)
3196 res = 0;
3198 if (duration < vmminmessage)
3199 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3200 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3201 else
3202 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3203 } else
3204 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3205 leave_vm_out:
3206 free_user(vmu);
3208 return res;
3211 #ifndef IMAP_STORAGE
3212 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3214 /* we know max messages, so stop process when number is hit */
3216 int x,dest;
3217 char sfn[PATH_MAX];
3218 char dfn[PATH_MAX];
3220 if (vm_lock_path(dir))
3221 return ERROR_LOCK_PATH;
3223 for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3224 make_file(sfn, sizeof(sfn), dir, x);
3225 if (EXISTS(dir, x, sfn, NULL)) {
3227 if (x != dest) {
3228 make_file(dfn, sizeof(dfn), dir, dest);
3229 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3232 dest++;
3235 ast_unlock_path(dir);
3237 return 0;
3239 #endif
3241 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3243 int d;
3244 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
3245 return d;
3248 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3250 #ifdef IMAP_STORAGE
3251 /* we must use mbox(x) folder names, and copy the message there */
3252 /* simple. huh? */
3253 long res;
3254 char sequence[10];
3256 /* if save to Old folder, just leave in INBOX */
3257 if (box == 1) return 10;
3258 /* get the real IMAP message number for this message */
3259 snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
3260 if (option_debug > 2)
3261 ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,(char *) mbox(box));
3262 res = mail_copy(vms->mailstream,sequence,(char *) mbox(box));
3263 if (res == 1) return 0;
3264 return 1;
3265 #else
3266 char *dir = vms->curdir;
3267 char *username = vms->username;
3268 char *context = vmu->context;
3269 char sfn[PATH_MAX];
3270 char dfn[PATH_MAX];
3271 char ddir[PATH_MAX];
3272 const char *dbox = mbox(box);
3273 int x;
3274 make_file(sfn, sizeof(sfn), dir, msg);
3275 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
3277 if (vm_lock_path(ddir))
3278 return ERROR_LOCK_PATH;
3280 for (x = 0; x < vmu->maxmsg; x++) {
3281 make_file(dfn, sizeof(dfn), ddir, x);
3282 if (!EXISTS(ddir, x, dfn, NULL))
3283 break;
3285 if (x >= vmu->maxmsg) {
3286 ast_unlock_path(ddir);
3287 return ERROR_MAILBOX_FULL;
3289 if (strcmp(sfn, dfn)) {
3290 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
3292 ast_unlock_path(ddir);
3293 #endif
3294 return 0;
3297 static int adsi_logo(unsigned char *buf)
3299 int bytes = 0;
3300 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
3301 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
3302 return bytes;
3305 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
3307 unsigned char buf[256];
3308 int bytes=0;
3309 int x;
3310 char num[5];
3312 *useadsi = 0;
3313 bytes += ast_adsi_data_mode(buf + bytes);
3314 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3316 bytes = 0;
3317 bytes += adsi_logo(buf);
3318 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3319 #ifdef DISPLAY
3320 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
3321 #endif
3322 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3323 bytes += ast_adsi_data_mode(buf + bytes);
3324 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3326 if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
3327 bytes = 0;
3328 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
3329 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3330 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3331 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3332 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3333 return 0;
3336 #ifdef DISPLAY
3337 /* Add a dot */
3338 bytes = 0;
3339 bytes += ast_adsi_logo(buf);
3340 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3341 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
3342 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3343 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3344 #endif
3345 bytes = 0;
3346 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
3347 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
3348 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
3349 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
3350 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
3351 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
3352 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3354 #ifdef DISPLAY
3355 /* Add another dot */
3356 bytes = 0;
3357 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
3358 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3360 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3361 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3362 #endif
3364 bytes = 0;
3365 /* These buttons we load but don't use yet */
3366 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
3367 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
3368 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
3369 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
3370 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
3371 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
3372 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3374 #ifdef DISPLAY
3375 /* Add another dot */
3376 bytes = 0;
3377 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
3378 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3379 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3380 #endif
3382 bytes = 0;
3383 for (x=0;x<5;x++) {
3384 snprintf(num, sizeof(num), "%d", x);
3385 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
3387 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
3388 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3390 #ifdef DISPLAY
3391 /* Add another dot */
3392 bytes = 0;
3393 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
3394 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3395 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3396 #endif
3398 if (ast_adsi_end_download(chan)) {
3399 bytes = 0;
3400 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
3401 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3402 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3403 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3404 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3405 return 0;
3407 bytes = 0;
3408 bytes += ast_adsi_download_disconnect(buf + bytes);
3409 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3410 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3412 if (option_debug)
3413 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
3415 #ifdef DISPLAY
3416 /* Add last dot */
3417 bytes = 0;
3418 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
3419 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3420 #endif
3421 if (option_debug)
3422 ast_log(LOG_DEBUG, "Restarting session...\n");
3424 bytes = 0;
3425 /* Load the session now */
3426 if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
3427 *useadsi = 1;
3428 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
3429 } else
3430 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
3432 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3433 return 0;
3436 static void adsi_begin(struct ast_channel *chan, int *useadsi)
3438 int x;
3439 if (!ast_adsi_available(chan))
3440 return;
3441 x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
3442 if (x < 0)
3443 return;
3444 if (!x) {
3445 if (adsi_load_vmail(chan, useadsi)) {
3446 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
3447 return;
3449 } else
3450 *useadsi = 1;
3453 static void adsi_login(struct ast_channel *chan)
3455 unsigned char buf[256];
3456 int bytes=0;
3457 unsigned char keys[8];
3458 int x;
3459 if (!ast_adsi_available(chan))
3460 return;
3462 for (x=0;x<8;x++)
3463 keys[x] = 0;
3464 /* Set one key for next */
3465 keys[3] = ADSI_KEY_APPS + 3;
3467 bytes += adsi_logo(buf + bytes);
3468 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
3469 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
3470 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3471 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
3472 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
3473 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
3474 bytes += ast_adsi_set_keys(buf + bytes, keys);
3475 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3476 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3479 static void adsi_password(struct ast_channel *chan)
3481 unsigned char buf[256];
3482 int bytes=0;
3483 unsigned char keys[8];
3484 int x;
3485 if (!ast_adsi_available(chan))
3486 return;
3488 for (x=0;x<8;x++)
3489 keys[x] = 0;
3490 /* Set one key for next */
3491 keys[3] = ADSI_KEY_APPS + 3;
3493 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3494 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
3495 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
3496 bytes += ast_adsi_set_keys(buf + bytes, keys);
3497 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3498 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3501 static void adsi_folders(struct ast_channel *chan, int start, char *label)
3503 unsigned char buf[256];
3504 int bytes=0;
3505 unsigned char keys[8];
3506 int x,y;
3508 if (!ast_adsi_available(chan))
3509 return;
3511 for (x=0;x<5;x++) {
3512 y = ADSI_KEY_APPS + 12 + start + x;
3513 if (y > ADSI_KEY_APPS + 12 + 4)
3514 y = 0;
3515 keys[x] = ADSI_KEY_SKT | y;
3517 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
3518 keys[6] = 0;
3519 keys[7] = 0;
3521 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
3522 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
3523 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3524 bytes += ast_adsi_set_keys(buf + bytes, keys);
3525 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3527 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3530 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
3532 int bytes=0;
3533 unsigned char buf[256];
3534 char buf1[256], buf2[256];
3535 char fn2[PATH_MAX];
3537 char cid[256]="";
3538 char *val;
3539 char *name, *num;
3540 char datetime[21]="";
3541 FILE *f;
3543 unsigned char keys[8];
3545 int x;
3547 if (!ast_adsi_available(chan))
3548 return;
3550 /* Retrieve important info */
3551 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
3552 f = fopen(fn2, "r");
3553 if (f) {
3554 while (!feof(f)) {
3555 fgets((char *)buf, sizeof(buf), f);
3556 if (!feof(f)) {
3557 char *stringp=NULL;
3558 stringp = (char *)buf;
3559 strsep(&stringp, "=");
3560 val = strsep(&stringp, "=");
3561 if (!ast_strlen_zero(val)) {
3562 if (!strcmp((char *)buf, "callerid"))
3563 ast_copy_string(cid, val, sizeof(cid));
3564 if (!strcmp((char *)buf, "origdate"))
3565 ast_copy_string(datetime, val, sizeof(datetime));
3569 fclose(f);
3571 /* New meaning for keys */
3572 for (x=0;x<5;x++)
3573 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3574 keys[6] = 0x0;
3575 keys[7] = 0x0;
3577 if (!vms->curmsg) {
3578 /* No prev key, provide "Folder" instead */
3579 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3581 if (vms->curmsg >= vms->lastmsg) {
3582 /* If last message ... */
3583 if (vms->curmsg) {
3584 /* but not only message, provide "Folder" instead */
3585 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3586 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3588 } else {
3589 /* Otherwise if only message, leave blank */
3590 keys[3] = 1;
3594 if (!ast_strlen_zero(cid)) {
3595 ast_callerid_parse(cid, &name, &num);
3596 if (!name)
3597 name = num;
3598 } else
3599 name = "Unknown Caller";
3601 /* If deleted, show "undeleted" */
3603 if (vms->deleted[vms->curmsg])
3604 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3606 /* Except "Exit" */
3607 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3608 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
3609 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
3610 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
3612 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3613 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3614 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
3615 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
3616 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3617 bytes += ast_adsi_set_keys(buf + bytes, keys);
3618 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3620 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3623 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
3625 int bytes=0;
3626 unsigned char buf[256];
3627 unsigned char keys[8];
3629 int x;
3631 if (!ast_adsi_available(chan))
3632 return;
3634 /* New meaning for keys */
3635 for (x=0;x<5;x++)
3636 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3638 keys[6] = 0x0;
3639 keys[7] = 0x0;
3641 if (!vms->curmsg) {
3642 /* No prev key, provide "Folder" instead */
3643 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3645 if (vms->curmsg >= vms->lastmsg) {
3646 /* If last message ... */
3647 if (vms->curmsg) {
3648 /* but not only message, provide "Folder" instead */
3649 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3650 } else {
3651 /* Otherwise if only message, leave blank */
3652 keys[3] = 1;
3656 /* If deleted, show "undeleted" */
3657 if (vms->deleted[vms->curmsg])
3658 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3660 /* Except "Exit" */
3661 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3662 bytes += ast_adsi_set_keys(buf + bytes, keys);
3663 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3665 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3668 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
3670 unsigned char buf[256] = "";
3671 char buf1[256] = "", buf2[256] = "";
3672 int bytes=0;
3673 unsigned char keys[8];
3674 int x;
3676 char *newm = (vms->newmessages == 1) ? "message" : "messages";
3677 char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
3678 if (!ast_adsi_available(chan))
3679 return;
3680 if (vms->newmessages) {
3681 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
3682 if (vms->oldmessages) {
3683 strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
3684 snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
3685 } else {
3686 snprintf(buf2, sizeof(buf2), "%s.", newm);
3688 } else if (vms->oldmessages) {
3689 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
3690 snprintf(buf2, sizeof(buf2), "%s.", oldm);
3691 } else {
3692 strcpy(buf1, "You have no messages.");
3693 buf2[0] = ' ';
3694 buf2[1] = '\0';
3696 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3697 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3698 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3700 for (x=0;x<6;x++)
3701 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3702 keys[6] = 0;
3703 keys[7] = 0;
3705 /* Don't let them listen if there are none */
3706 if (vms->lastmsg < 0)
3707 keys[0] = 1;
3708 bytes += ast_adsi_set_keys(buf + bytes, keys);
3710 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3712 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3715 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
3717 unsigned char buf[256] = "";
3718 char buf1[256] = "", buf2[256] = "";
3719 int bytes=0;
3720 unsigned char keys[8];
3721 int x;
3723 char *mess = (vms->lastmsg == 0) ? "message" : "messages";
3725 if (!ast_adsi_available(chan))
3726 return;
3728 /* Original command keys */
3729 for (x=0;x<6;x++)
3730 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3732 keys[6] = 0;
3733 keys[7] = 0;
3735 if ((vms->lastmsg + 1) < 1)
3736 keys[0] = 0;
3738 snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
3739 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
3741 if (vms->lastmsg + 1)
3742 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
3743 else
3744 strcpy(buf2, "no messages.");
3745 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3746 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3747 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
3748 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3749 bytes += ast_adsi_set_keys(buf + bytes, keys);
3751 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3753 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3758 static void adsi_clear(struct ast_channel *chan)
3760 char buf[256];
3761 int bytes=0;
3762 if (!ast_adsi_available(chan))
3763 return;
3764 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3765 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3767 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3771 static void adsi_goodbye(struct ast_channel *chan)
3773 unsigned char buf[256];
3774 int bytes=0;
3776 if (!ast_adsi_available(chan))
3777 return;
3778 bytes += adsi_logo(buf + bytes);
3779 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
3780 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
3781 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3782 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3784 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3787 /*--- get_folder: Folder menu ---*/
3788 /* Plays "press 1 for INBOX messages" etc
3789 Should possibly be internationalized
3791 static int get_folder(struct ast_channel *chan, int start)
3793 int x;
3794 int d;
3795 char fn[PATH_MAX];
3796 d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
3797 if (d)
3798 return d;
3799 for (x = start; x< 5; x++) { /* For all folders */
3800 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
3801 return d;
3802 d = ast_play_and_wait(chan, "vm-for"); /* "for" */
3803 if (d)
3804 return d;
3805 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
3806 d = vm_play_folder_name(chan, fn);
3807 if (d)
3808 return d;
3809 d = ast_waitfordigit(chan, 500);
3810 if (d)
3811 return d;
3813 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
3814 if (d)
3815 return d;
3816 d = ast_waitfordigit(chan, 4000);
3817 return d;
3820 static int get_folder2(struct ast_channel *chan, char *fn, int start)
3822 int res = 0;
3823 res = ast_play_and_wait(chan, fn); /* Folder name */
3824 while (((res < '0') || (res > '9')) &&
3825 (res != '#') && (res >= 0)) {
3826 res = get_folder(chan, 0);
3828 return res;
3831 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfmts,
3832 char *context, signed char record_gain, long *duration, struct vm_state *vms)
3834 int cmd = 0;
3835 int retries = 0, prepend_duration = 0, already_recorded = 0;
3836 signed char zero_gain = 0;
3837 struct ast_config *msg_cfg;
3838 const char *duration_str;
3839 char msgfile[PATH_MAX], backup[PATH_MAX];
3840 char textfile[PATH_MAX];
3842 /* Must always populate duration correctly */
3843 make_file(msgfile, sizeof(msgfile), curdir, curmsg);
3844 strcpy(textfile, msgfile);
3845 strcpy(backup, msgfile);
3846 strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
3847 strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
3849 if (!(msg_cfg = ast_config_load(textfile))) {
3850 return -1;
3853 *duration = 0;
3854 if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
3855 *duration = atoi(duration_str);
3857 while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
3858 if (cmd)
3859 retries = 0;
3860 switch (cmd) {
3861 case '1':
3862 /* prepend a message to the current message, update the metadata and return */
3864 prepend_duration = 0;
3866 /* if we can't read the message metadata, stop now */
3867 if (!msg_cfg) {
3868 cmd = 0;
3869 break;
3872 /* Back up the original file, so we can retry the prepend */
3873 if (already_recorded)
3874 ast_filecopy(backup, msgfile, NULL);
3875 else
3876 ast_filecopy(msgfile, backup, NULL);
3877 already_recorded = 1;
3879 if (record_gain)
3880 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
3882 cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vmfmts, &prepend_duration, 1, silencethreshold, maxsilence);
3883 if (record_gain)
3884 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
3886 if (prepend_duration) {
3887 struct ast_category *msg_cat;
3888 /* need enough space for a maximum-length message duration */
3889 char duration_str[12];
3891 prepend_duration += *duration;
3892 msg_cat = ast_category_get(msg_cfg, "message");
3893 snprintf(duration_str, 11, "%d", prepend_duration);
3894 if (!ast_variable_update(msg_cat, "duration", duration_str, NULL, 0)) {
3895 config_text_file_save(textfile, msg_cfg, "app_voicemail");
3896 STORE(curdir, vmu->mailbox, context, curmsg, chan, vmu, vmfmts, prepend_duration, vms);
3900 break;
3902 case '2':
3903 cmd = 't';
3904 break;
3905 case '*':
3906 cmd = '*';
3907 break;
3908 default:
3909 cmd = ast_play_and_wait(chan,"vm-forwardoptions");
3910 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
3911 if (!cmd)
3912 cmd = ast_play_and_wait(chan,"vm-starmain");
3913 /* "press star to return to the main menu" */
3914 if (!cmd)
3915 cmd = ast_waitfordigit(chan,6000);
3916 if (!cmd)
3917 retries++;
3918 if (retries > 3)
3919 cmd = 't';
3923 ast_config_destroy(msg_cfg);
3924 if (already_recorded)
3925 ast_filedelete(backup, NULL);
3926 if (prepend_duration)
3927 *duration = prepend_duration;
3929 if (cmd == 't' || cmd == 'S')
3930 cmd = 0;
3931 return cmd;
3934 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
3936 char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
3937 int newmsgs = 0, oldmsgs = 0;
3938 const char *category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3940 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
3941 make_file(fn, sizeof(fn), todir, msgnum);
3942 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
3944 if (!ast_strlen_zero(vmu->attachfmt)) {
3945 if (strstr(fmt, vmu->attachfmt)) {
3946 fmt = vmu->attachfmt;
3947 } else {
3948 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);
3952 /* Attach only the first format */
3953 fmt = ast_strdupa(fmt);
3954 stringp = fmt;
3955 strsep(&stringp, "|");
3957 if (!ast_strlen_zero(vmu->email)) {
3958 int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
3959 char *myserveremail = serveremail;
3960 attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
3961 if (!ast_strlen_zero(vmu->serveremail))
3962 myserveremail = vmu->serveremail;
3964 if (attach_user_voicemail)
3965 RETRIEVE(todir, msgnum);
3967 /*XXX possible imap issue, should category be NULL XXX*/
3968 sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail, chan, category);
3970 if (attach_user_voicemail)
3971 DISPOSE(todir, msgnum);
3974 if (!ast_strlen_zero(vmu->pager)) {
3975 char *myserveremail = serveremail;
3976 if (!ast_strlen_zero(vmu->serveremail))
3977 myserveremail = vmu->serveremail;
3978 sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, duration, vmu, category);
3981 if (ast_test_flag(vmu, VM_DELETE)) {
3982 DELETE(todir, msgnum, fn);
3985 #ifdef IMAP_STORAGE
3986 DELETE(todir, msgnum, fn);
3987 #endif
3988 /* Leave voicemail for someone */
3989 if (ast_app_has_voicemail(ext_context, NULL)) {
3990 ast_app_inboxcount(ext_context, &newmsgs, &oldmsgs);
3992 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);
3993 run_externnotify(vmu->context, vmu->mailbox);
3994 return 0;
3997 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)
3999 #ifdef IMAP_STORAGE
4000 BODY *body;
4001 char *header_content;
4002 char *temp;
4003 char todir[256];
4004 int todircount=0;
4005 struct vm_state *dstvms;
4006 #endif
4007 char username[70]="";
4008 int res = 0, cmd = 0;
4009 struct ast_vm_user *receiver = NULL, *vmtmp;
4010 AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
4011 char *stringp;
4012 const char *s;
4013 int saved_messages = 0, found = 0;
4014 int valid_extensions = 0;
4015 char *dir;
4016 int curmsg;
4018 if (vms == NULL) return -1;
4019 dir = vms->curdir;
4020 curmsg = vms->curmsg;
4022 while (!res && !valid_extensions) {
4023 int use_directory = 0;
4024 if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
4025 int done = 0;
4026 int retries = 0;
4027 cmd=0;
4028 while ((cmd >= 0) && !done ){
4029 if (cmd)
4030 retries = 0;
4031 switch (cmd) {
4032 case '1':
4033 use_directory = 0;
4034 done = 1;
4035 break;
4036 case '2':
4037 use_directory = 1;
4038 done=1;
4039 break;
4040 case '*':
4041 cmd = 't';
4042 done = 1;
4043 break;
4044 default:
4045 /* Press 1 to enter an extension press 2 to use the directory */
4046 cmd = ast_play_and_wait(chan,"vm-forward");
4047 if (!cmd)
4048 cmd = ast_waitfordigit(chan,3000);
4049 if (!cmd)
4050 retries++;
4051 if (retries > 3)
4053 cmd = 't';
4054 done = 1;
4059 if (cmd < 0 || cmd == 't')
4060 break;
4063 if (use_directory) {
4064 /* use app_directory */
4066 char old_context[sizeof(chan->context)];
4067 char old_exten[sizeof(chan->exten)];
4068 int old_priority;
4069 struct ast_app* app;
4072 app = pbx_findapp("Directory");
4073 if (app) {
4074 char vmcontext[256];
4075 /* make backup copies */
4076 memcpy(old_context, chan->context, sizeof(chan->context));
4077 memcpy(old_exten, chan->exten, sizeof(chan->exten));
4078 old_priority = chan->priority;
4080 /* call the the Directory, changes the channel */
4081 snprintf(vmcontext, sizeof(vmcontext), "%s||v", context ? context : "default");
4082 res = pbx_exec(chan, app, vmcontext);
4084 ast_copy_string(username, chan->exten, sizeof(username));
4086 /* restore the old context, exten, and priority */
4087 memcpy(chan->context, old_context, sizeof(chan->context));
4088 memcpy(chan->exten, old_exten, sizeof(chan->exten));
4089 chan->priority = old_priority;
4091 } else {
4092 ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
4093 ast_clear_flag((&globalflags), VM_DIRECFORWARD);
4095 } else {
4096 /* Ask for an extension */
4097 res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
4098 if (res)
4099 break;
4100 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
4101 break;
4104 /* start all over if no username */
4105 if (ast_strlen_zero(username))
4106 continue;
4107 stringp = username;
4108 s = strsep(&stringp, "*");
4109 /* start optimistic */
4110 valid_extensions = 1;
4111 while (s) {
4112 /* Don't forward to ourselves but allow leaving a message for ourselves (flag == 1). find_user is going to malloc since we have a NULL as first argument */
4113 if ((flag == 1 || strcmp(s,sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
4114 AST_LIST_INSERT_HEAD(&extensions, receiver, list);
4115 found++;
4116 } else {
4117 valid_extensions = 0;
4118 break;
4120 s = strsep(&stringp, "*");
4122 /* break from the loop of reading the extensions */
4123 if (valid_extensions)
4124 break;
4125 /* "I am sorry, that's not a valid extension. Please try again." */
4126 res = ast_play_and_wait(chan, "pbx-invalid");
4128 /* check if we're clear to proceed */
4129 if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
4130 return res;
4131 if (flag==1) {
4132 struct leave_vm_options leave_options;
4133 char mailbox[AST_MAX_EXTENSION * 2 + 2];
4134 /* Make sure that context doesn't get set as a literal "(null)" (or else find_user won't find it) */
4135 if (context)
4136 snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
4137 else
4138 ast_copy_string(mailbox, username, sizeof(mailbox));
4140 /* Send VoiceMail */
4141 memset(&leave_options, 0, sizeof(leave_options));
4142 leave_options.record_gain = record_gain;
4143 cmd = leave_voicemail(chan, mailbox, &leave_options);
4144 } else {
4145 /* Forward VoiceMail */
4146 long duration = 0;
4147 char origmsgfile[PATH_MAX], msgfile[PATH_MAX];
4148 struct vm_state vmstmp;
4150 memcpy(&vmstmp, vms, sizeof(vmstmp));
4152 make_file(origmsgfile, sizeof(origmsgfile), dir, curmsg);
4153 create_dirpath(vmstmp.curdir, sizeof(vmstmp.curdir), sender->context, vmstmp.username, "tmp");
4154 make_file(msgfile, sizeof(msgfile), vmstmp.curdir, curmsg);
4156 RETRIEVE(dir, curmsg);
4158 /* Alter a surrogate file, only */
4159 copy_plain_file(origmsgfile, msgfile);
4161 cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp);
4162 if (!cmd) {
4163 AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
4164 #ifdef IMAP_STORAGE
4165 char *myserveremail;
4166 int attach_user_voicemail;
4167 /* Need to get message content */
4168 if (option_debug > 2)
4169 ast_log (LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", vms->curmsg, vms->msgArray[vms->curmsg]);
4170 if (vms->msgArray[vms->curmsg] == 0) {
4171 ast_log (LOG_WARNING,"Trying to access unknown message\n");
4172 return -1;
4175 /* This will only work for new messages... */
4176 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
4177 /* empty string means no valid header */
4178 if (ast_strlen_zero(header_content)) {
4179 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
4180 return -1;
4182 /* Get header info needed by sendmail */
4183 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:");
4184 if (temp)
4185 duration = atoi(temp);
4186 else
4187 duration = 0;
4189 /* Attach only the first format */
4190 fmt = ast_strdupa(fmt);
4191 if (fmt) {
4192 stringp = fmt;
4193 strsep(&stringp, "|");
4194 } else {
4195 ast_log (LOG_ERROR,"audio format not set. Default to WAV\n");
4196 fmt = "WAV";
4198 if (!strcasecmp(fmt, "wav49"))
4199 fmt = "WAV";
4200 if (option_debug > 2)
4201 ast_log (LOG_DEBUG,"**** format set to %s, vmfmts set to %s\n",fmt,vmfmts);
4202 /* ast_copy_string(fmt, vmfmts, sizeof(fmt));*/
4203 /* if (!ast_strlen_zero(fmt)) { */
4204 snprintf(todir, sizeof(todir), "%s%s/%s/tmp", VM_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
4205 make_gsm_file(vms->fn, sizeof(vms->fn), vms->imapuser, todir, vms->curmsg);
4206 if (option_debug > 2)
4207 ast_log (LOG_DEBUG,"Before mail_fetchstructure, message number is %ld, filename is:%s\n",vms->msgArray[vms->curmsg], vms->fn);
4208 /*mail_fetchstructure (mailstream, vmArray[0], &body); */
4209 mail_fetchstructure (vms->mailstream, vms->msgArray[vms->curmsg], &body);
4210 save_body(body,vms,"3","gsm");
4211 /* should not assume "fmt" here! */
4212 save_body(body,vms,"2",fmt);
4214 /* get destination mailbox */
4215 dstvms = get_vm_state_by_mailbox(vmtmp->mailbox,0);
4216 if (dstvms) {
4217 init_mailstream(dstvms, 0);
4218 if (!dstvms->mailstream) {
4219 ast_log (LOG_ERROR,"IMAP mailstream for %s is NULL\n",vmtmp->mailbox);
4220 } else {
4221 STORE(todir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms);
4222 run_externnotify(vmtmp->context, vmtmp->mailbox);
4224 } else {
4225 ast_log (LOG_ERROR,"Could not find state information for mailbox %s\n",vmtmp->mailbox);
4228 myserveremail = serveremail;
4229 if (!ast_strlen_zero(vmtmp->serveremail))
4230 myserveremail = vmtmp->serveremail;
4231 attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
4232 attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
4233 /* NULL category for IMAP storage */
4234 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);
4235 #else
4236 copy_message(chan, sender, -1, curmsg, duration, vmtmp, fmt, vmstmp.curdir);
4237 #endif
4238 saved_messages++;
4239 AST_LIST_REMOVE_CURRENT(&extensions, list);
4240 free_user(vmtmp);
4241 if (res)
4242 break;
4244 AST_LIST_TRAVERSE_SAFE_END;
4245 if (saved_messages > 0) {
4246 /* give confirmation that the message was saved */
4247 /* commented out since we can't forward batches yet
4248 if (saved_messages == 1)
4249 res = ast_play_and_wait(chan, "vm-message");
4250 else
4251 res = ast_play_and_wait(chan, "vm-messages");
4252 if (!res)
4253 res = ast_play_and_wait(chan, "vm-saved"); */
4254 res = ast_play_and_wait(chan, "vm-msgsaved");
4258 /* Remove surrogate file */
4259 vm_delete(msgfile);
4262 /* If anything failed above, we still have this list to free */
4263 while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list)))
4264 free_user(vmtmp);
4265 return res ? res : cmd;
4268 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
4270 int res;
4271 if ((res = ast_stream_and_wait(chan, file, chan->language, AST_DIGIT_ANY)) < 0)
4272 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
4273 return res;
4276 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
4278 return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", "2", skipms);
4281 static int play_message_category(struct ast_channel *chan, const char *category)
4283 int res = 0;
4285 if (!ast_strlen_zero(category))
4286 res = ast_play_and_wait(chan, category);
4288 if (res) {
4289 ast_log(LOG_WARNING, "No sound file for category '%s' was found.\n", category);
4290 res = 0;
4293 return res;
4296 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
4298 int res = 0;
4299 struct vm_zone *the_zone = NULL;
4300 time_t t;
4302 if (ast_get_time_t(origtime, &t, 0, NULL)) {
4303 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
4304 return 0;
4307 /* Does this user have a timezone specified? */
4308 if (!ast_strlen_zero(vmu->zonetag)) {
4309 /* Find the zone in the list */
4310 struct vm_zone *z;
4311 AST_LIST_LOCK(&zones);
4312 AST_LIST_TRAVERSE(&zones, z, list) {
4313 if (!strcmp(z->name, vmu->zonetag)) {
4314 the_zone = z;
4315 break;
4318 AST_LIST_UNLOCK(&zones);
4321 /* No internal variable parsing for now, so we'll comment it out for the time being */
4322 #if 0
4323 /* Set the DIFF_* variables */
4324 ast_localtime(&t, &time_now, NULL);
4325 tv_now = ast_tvnow();
4326 tnow = tv_now.tv_sec;
4327 ast_localtime(&tnow, &time_then, NULL);
4329 /* Day difference */
4330 if (time_now.tm_year == time_then.tm_year)
4331 snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
4332 else
4333 snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
4334 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
4336 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
4337 #endif
4338 if (the_zone)
4339 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
4340 else if (!strcasecmp(chan->language,"pl")) /* POLISH syntax */
4341 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
4342 else if (!strcasecmp(chan->language,"se")) /* SWEDISH syntax */
4343 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
4344 else if (!strcasecmp(chan->language,"no")) /* NORWEGIAN syntax */
4345 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
4346 else if (!strcasecmp(chan->language,"de")) /* GERMAN syntax */
4347 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
4348 else if (!strcasecmp(chan->language,"nl")) /* DUTCH syntax */
4349 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
4350 else if (!strcasecmp(chan->language,"it")) /* ITALIAN syntax */
4351 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);
4352 else if (!strcasecmp(chan->language,"gr"))
4353 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q H 'digits/kai' M ", NULL);
4354 else if (!strcasecmp(chan->language,"pt_BR"))
4355 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);
4356 else
4357 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
4358 #if 0
4359 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
4360 #endif
4361 return res;
4366 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
4368 int res = 0;
4369 int i;
4370 char *callerid, *name;
4371 char prefile[PATH_MAX] = "";
4374 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
4375 /* 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 */
4376 if ((cid == NULL)||(context == NULL))
4377 return res;
4379 /* Strip off caller ID number from name */
4380 if (option_debug > 2)
4381 ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
4382 ast_callerid_parse(cid, &name, &callerid);
4383 if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
4384 /* Check for internal contexts and only */
4385 /* say extension when the call didn't come from an internal context in the list */
4386 for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
4387 if (option_debug > 2)
4388 ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
4389 if ((strcmp(cidinternalcontexts[i], context) == 0))
4390 break;
4392 if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
4393 if (!res) {
4394 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
4395 if (!ast_strlen_zero(prefile)) {
4396 /* See if we can find a recorded name for this person instead of their extension number */
4397 if (ast_fileexists(prefile, NULL, NULL) > 0) {
4398 if (option_verbose > 2)
4399 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
4400 if (!callback)
4401 res = wait_file2(chan, vms, "vm-from");
4402 res = ast_stream_and_wait(chan, prefile, chan->language, "");
4403 } else {
4404 if (option_verbose > 2)
4405 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
4406 /* BB: Say "from extension" as one saying to sound smoother */
4407 if (!callback)
4408 res = wait_file2(chan, vms, "vm-from-extension");
4409 res = ast_say_digit_str(chan, callerid, "", chan->language);
4415 else if (!res){
4416 if (option_debug > 2)
4417 ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
4418 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
4419 if (!callback)
4420 res = wait_file2(chan, vms, "vm-from-phonenumber");
4421 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
4423 } else {
4424 /* Number unknown */
4425 if (option_debug)
4426 ast_log(LOG_DEBUG, "VM-CID: From an unknown number\n");
4427 /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
4428 res = wait_file2(chan, vms, "vm-unknown-caller");
4430 return res;
4433 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
4435 int res = 0;
4436 int durationm;
4437 int durations;
4438 /* Verify that we have a duration for the message */
4439 if (duration == NULL)
4440 return res;
4442 /* Convert from seconds to minutes */
4443 durations=atoi(duration);
4444 durationm=(durations / 60);
4446 if (option_debug > 2)
4447 ast_log(LOG_DEBUG, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
4449 if ((!res) && (durationm >= minduration)) {
4450 res = wait_file2(chan, vms, "vm-duration");
4452 /* POLISH syntax */
4453 if (!strcasecmp(chan->language, "pl")) {
4454 div_t num = div(durationm, 10);
4456 if (durationm == 1) {
4457 res = ast_play_and_wait(chan, "digits/1z");
4458 res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
4459 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
4460 if (num.rem == 2) {
4461 if (!num.quot) {
4462 res = ast_play_and_wait(chan, "digits/2-ie");
4463 } else {
4464 res = say_and_wait(chan, durationm - 2 , chan->language);
4465 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
4467 } else {
4468 res = say_and_wait(chan, durationm, chan->language);
4470 res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
4471 } else {
4472 res = say_and_wait(chan, durationm, chan->language);
4473 res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
4475 /* DEFAULT syntax */
4476 } else {
4477 res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
4478 res = wait_file2(chan, vms, "vm-minutes");
4481 return res;
4484 #ifdef IMAP_STORAGE
4485 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
4487 BODY *body;
4488 char *header_content;
4489 char cid[256];
4490 char context[256];
4491 char origtime[32];
4492 char duration[16];
4493 char category[32];
4494 char todir[PATH_MAX];
4495 int res = 0;
4496 char *attachedfilefmt;
4497 char *temp;
4499 vms->starting = 0;
4500 if (option_debug > 2)
4501 ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
4502 if (vms->msgArray[vms->curmsg] == 0) {
4503 ast_log (LOG_WARNING,"Trying to access unknown message\n");
4504 return -1;
4507 /* This will only work for new messages... */
4508 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
4509 /* empty string means no valid header */
4510 if (ast_strlen_zero(header_content)) {
4511 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
4512 return -1;
4514 snprintf(todir, sizeof(todir), "%s%s/%s/tmp", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
4515 make_gsm_file(vms->fn, sizeof(vms->fn), vms->imapuser, todir, vms->curmsg);
4517 mail_fetchstructure (vms->mailstream,vms->msgArray[vms->curmsg],&body);
4519 /* We have the body, now we extract the file name of the first attachment. */
4520 if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
4521 attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
4522 } else {
4523 ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
4524 return -1;
4527 /* Find the format of the attached file */
4529 strsep(&attachedfilefmt, ".");
4530 if (!attachedfilefmt) {
4531 ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
4532 return -1;
4534 save_body(body, vms, "2", attachedfilefmt);
4536 adsi_message(chan, vms);
4537 if (!vms->curmsg)
4538 res = wait_file2(chan, vms, "vm-first"); /* "First" */
4539 else if (vms->curmsg == vms->lastmsg)
4540 res = wait_file2(chan, vms, "vm-last"); /* "last" */
4541 if (!res) {
4542 res = wait_file2(chan, vms, "vm-message"); /* "message" */
4543 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
4544 if (!res)
4545 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
4549 /* Get info from headers!! */
4550 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:");
4552 if (temp)
4553 ast_copy_string(cid, temp, sizeof(cid));
4554 else
4555 cid[0] = '\0';
4557 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Context:");
4559 if (temp)
4560 ast_copy_string(context, temp, sizeof(context));
4561 else
4562 context[0] = '\0';
4564 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:");
4566 if (temp)
4567 ast_copy_string(origtime, temp, sizeof(origtime));
4568 else
4569 origtime[0] = '\0';
4571 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:");
4573 if (temp)
4574 ast_copy_string(duration,temp, sizeof(duration));
4575 else
4576 duration[0] = '\0';
4578 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Category:");
4580 if (temp)
4581 ast_copy_string(category,temp, sizeof(category));
4582 else
4583 category[0] = '\0';
4585 /*if (!strncasecmp("macro",context,5)) Macro names in contexts are useless for our needs */
4586 /* context = ast_variable_retrieve(msg_cfg, "message","macrocontext"); */
4587 if (res == '1')
4588 res = 0;
4590 if ((!res) && !ast_strlen_zero(category)) {
4591 res = play_message_category(chan, category);
4594 if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)) && origtime[0] != '\0')
4595 res = play_message_datetime(chan, vmu, origtime, "IMAP_STORAGE");
4596 if ((!res) && (ast_test_flag(vmu, VM_SAYCID)) && cid[0] !='\0' && context[0] !='\0')
4597 res = play_message_callerid(chan, vms, cid, context, 0);
4599 if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)) && duration[0] != '\0')
4600 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
4602 /* Allow pressing '1' to skip envelope / callerid */
4603 /* if (res == '1')
4604 res = 0;
4606 /*ast_config_destroy(msg_cfg);*/
4607 res = 0;
4609 if (!res) {
4610 vms->heard[vms->curmsg] = 1;
4611 res = wait_file(chan, vms, vms->fn);
4613 DISPOSE(vms->curdir, vms->curmsg);
4614 DELETE(0, 0, vms->fn);
4615 return res;
4617 #else
4618 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
4620 int res = 0;
4621 char filename[256], *cid;
4622 const char *origtime, *context, *category, *duration;
4623 struct ast_config *msg_cfg;
4625 vms->starting = 0;
4626 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
4627 adsi_message(chan, vms);
4628 if (!vms->curmsg)
4629 res = wait_file2(chan, vms, "vm-first"); /* "First" */
4630 else if (vms->curmsg == vms->lastmsg)
4631 res = wait_file2(chan, vms, "vm-last"); /* "last" */
4632 if (!res) {
4633 /* POLISH syntax */
4634 if (!strcasecmp(chan->language, "pl")) {
4635 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
4636 int ten, one;
4637 char nextmsg[256];
4638 ten = (vms->curmsg + 1) / 10;
4639 one = (vms->curmsg + 1) % 10;
4641 if (vms->curmsg < 20) {
4642 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
4643 res = wait_file2(chan, vms, nextmsg);
4644 } else {
4645 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
4646 res = wait_file2(chan, vms, nextmsg);
4647 if (one > 0) {
4648 if (!res) {
4649 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
4650 res = wait_file2(chan, vms, nextmsg);
4655 if (!res)
4656 res = wait_file2(chan, vms, "vm-message");
4657 } else {
4658 if (!strcasecmp(chan->language, "se")) /* SWEDISH syntax */
4659 res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
4660 else /* DEFAULT syntax */
4661 res = wait_file2(chan, vms, "vm-message");
4662 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
4663 if (!res)
4664 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
4669 /* Retrieve info from VM attribute file */
4670 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
4671 snprintf(filename, sizeof(filename), "%s.txt", vms->fn2);
4672 RETRIEVE(vms->curdir, vms->curmsg);
4673 msg_cfg = ast_config_load(filename);
4674 if (!msg_cfg) {
4675 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
4676 return 0;
4679 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
4680 ast_log(LOG_WARNING, "No origtime?!\n");
4681 DISPOSE(vms->curdir, vms->curmsg);
4682 ast_config_destroy(msg_cfg);
4683 return 0;
4686 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
4687 duration = ast_variable_retrieve(msg_cfg, "message", "duration");
4688 category = ast_variable_retrieve(msg_cfg, "message", "category");
4690 context = ast_variable_retrieve(msg_cfg, "message", "context");
4691 if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
4692 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
4693 if (!res)
4694 res = play_message_category(chan, category);
4695 if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
4696 res = play_message_datetime(chan, vmu, origtime, filename);
4697 if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
4698 res = play_message_callerid(chan, vms, cid, context, 0);
4699 if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
4700 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
4701 /* Allow pressing '1' to skip envelope / callerid */
4702 if (res == '1')
4703 res = 0;
4704 ast_config_destroy(msg_cfg);
4706 if (!res) {
4707 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
4708 vms->heard[vms->curmsg] = 1;
4709 if ((res = wait_file(chan, vms, vms->fn)) < 0) {
4710 ast_log(LOG_WARNING, "Playback of message %s failed\n", vms->fn);
4711 res = 0;
4714 DISPOSE(vms->curdir, vms->curmsg);
4715 return res;
4717 #endif
4719 #ifdef IMAP_STORAGE
4720 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
4722 char tmp[256], *t = tmp;
4723 size_t left = sizeof(tmp);
4725 if (box == 1) {
4726 ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
4727 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(1));
4728 } else {
4729 ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
4730 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
4733 /* Build up server information */
4734 ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
4736 /* Add authentication user if present */
4737 if (!ast_strlen_zero(authuser))
4738 ast_build_string(&t, &left, "/authuser=%s", authuser);
4740 /* Add flags if present */
4741 if (!ast_strlen_zero(imapflags))
4742 ast_build_string(&t, &left, "/%s", imapflags);
4744 /* End with username */
4745 ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
4747 if (box == 0 || box == 1)
4748 snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
4749 else
4750 snprintf(spec, len, "%s%s%c%s", tmp, imapfolder, delimiter, mbox(box));
4753 static int init_mailstream(struct vm_state *vms, int box)
4755 MAILSTREAM *stream = NIL;
4756 long debug;
4757 char tmp[256];
4759 if (!vms) {
4760 ast_log (LOG_ERROR,"vm_state is NULL!\n");
4761 return -1;
4763 if (option_debug > 2)
4764 ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
4765 if (vms->mailstream == NIL || !vms->mailstream) {
4766 if (option_debug)
4767 ast_log (LOG_DEBUG,"mailstream not set.\n");
4768 } else {
4769 stream = vms->mailstream;
4771 /* debug = T; user wants protocol telemetry? */
4772 debug = NIL; /* NO protocol telemetry? */
4774 if (delimiter == '\0') { /* did not probe the server yet */
4775 char *cp;
4776 #ifdef USE_SYSTEM_IMAP
4777 #include <imap/linkage.c>
4778 #elif defined(USE_SYSTEM_CCLIENT)
4779 #include <c-client/linkage.c>
4780 #else
4781 #include "linkage.c"
4782 #endif
4783 /* Connect to INBOX first to get folders delimiter */
4784 imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
4785 ast_mutex_lock(&vms->lock);
4786 stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
4787 ast_mutex_unlock(&vms->lock);
4788 if (stream == NIL) {
4789 ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
4790 return -1;
4792 get_mailbox_delimiter(stream);
4793 /* update delimiter in imapfolder */
4794 for (cp = imapfolder; *cp; cp++)
4795 if (*cp == '/')
4796 *cp = delimiter;
4798 /* Now connect to the target folder */
4799 imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
4800 if (option_debug > 2)
4801 ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
4802 ast_mutex_lock(&vms->lock);
4803 vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
4804 ast_mutex_unlock(&vms->lock);
4805 if (vms->mailstream == NIL) {
4806 return -1;
4807 } else {
4808 return 0;
4812 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
4814 SEARCHPGM *pgm;
4815 SEARCHHEADER *hdr;
4816 int ret;
4818 ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
4819 if (option_debug > 2)
4820 ast_log(LOG_DEBUG,"Before init_mailstream, user is %s\n",vmu->imapuser);
4821 ret = init_mailstream(vms, box);
4822 if (ret != 0 || !vms->mailstream) {
4823 ast_log (LOG_ERROR,"Could not initialize mailstream\n");
4824 return -1;
4827 /* Check Quota */
4828 if (box == 0) {
4829 if (option_debug > 2)
4830 ast_log(LOG_DEBUG, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
4831 check_quota(vms,(char *)mbox(box));
4834 pgm = mail_newsearchpgm();
4836 /* Check IMAP folder for Asterisk messages only... */
4837 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", vmu->mailbox);
4838 pgm->header = hdr;
4839 pgm->deleted = 0;
4840 pgm->undeleted = 1;
4842 /* if box = 0, check for new, if box = 1, check for read */
4843 if (box == 0) {
4844 pgm->unseen = 1;
4845 pgm->seen = 0;
4846 } else if (box == 1) {
4847 pgm->seen = 1;
4848 pgm->unseen = 0;
4851 vms->vmArrayIndex = 0;
4852 if (option_debug > 2)
4853 ast_log(LOG_DEBUG,"Before mail_search_full, user is %s\n",vmu->imapuser);
4854 mail_search_full (vms->mailstream, NULL, pgm, NIL);
4857 vms->lastmsg = vms->vmArrayIndex - 1;
4859 mail_free_searchpgm(&pgm);
4860 return 0;
4862 #else
4863 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
4865 int res = 0;
4866 int count_msg, last_msg;
4868 ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
4870 /* Rename the member vmbox HERE so that we don't try to return before
4871 * we know what's going on.
4873 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
4875 /* Faster to make the directory than to check if it exists. */
4876 create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
4878 count_msg = count_messages(vmu, vms->curdir);
4879 if (count_msg < 0)
4880 return count_msg;
4881 else
4882 vms->lastmsg = count_msg - 1;
4885 The following test is needed in case sequencing gets messed up.
4886 There appears to be more than one way to mess up sequence, so
4887 we will not try to find all of the root causes--just fix it when
4888 detected.
4891 last_msg = last_message_index(vmu, vms->curdir);
4892 if (last_msg < 0)
4893 return last_msg;
4894 else if (vms->lastmsg != last_msg)
4896 ast_log(LOG_NOTICE, "Resequencing Mailbox: %s\n", vms->curdir);
4897 res = resequence_mailbox(vmu, vms->curdir);
4898 if (res)
4899 return res;
4902 return 0;
4904 #endif
4906 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
4908 int x = 0;
4909 #ifndef IMAP_STORAGE
4910 int res = 0, nummsg;
4911 #endif
4913 if (vms->lastmsg <= -1)
4914 goto done;
4916 vms->curmsg = -1;
4917 #ifndef IMAP_STORAGE
4918 /* Get the deleted messages fixed */
4919 if (vm_lock_path(vms->curdir))
4920 return ERROR_LOCK_PATH;
4922 for (x = 0; x < vmu->maxmsg; x++) {
4923 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
4924 /* Save this message. It's not in INBOX or hasn't been heard */
4925 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
4926 if (!EXISTS(vms->curdir, x, vms->fn, NULL))
4927 break;
4928 vms->curmsg++;
4929 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
4930 if (strcmp(vms->fn, vms->fn2)) {
4931 RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
4933 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
4934 /* Move to old folder before deleting */
4935 res = save_to_folder(vmu, vms, x, 1);
4936 if (res == ERROR_LOCK_PATH || res == ERROR_MAILBOX_FULL) {
4937 /* If save failed do not delete the message */
4938 ast_log(LOG_WARNING, "Save failed. Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
4939 vms->deleted[x] = 0;
4940 vms->heard[x] = 0;
4941 --x;
4946 /* Delete ALL remaining messages */
4947 nummsg = x - 1;
4948 for (x = vms->curmsg + 1; x <= nummsg; x++) {
4949 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
4950 if (EXISTS(vms->curdir, x, vms->fn, NULL))
4951 DELETE(vms->curdir, x, vms->fn);
4953 ast_unlock_path(vms->curdir);
4954 #else
4955 if (vms->deleted) {
4956 for (x=0;x < vmu->maxmsg;x++) {
4957 if (vms->deleted[x]) {
4958 if (option_debug > 2)
4959 ast_log(LOG_DEBUG,"IMAP delete of %d\n",x);
4960 IMAP_DELETE(vms->curdir, x, vms->fn, vms);
4964 #endif
4966 done:
4967 if (vms->deleted)
4968 memset(vms->deleted, 0, vmu->maxmsg * sizeof(int));
4969 if (vms->heard)
4970 memset(vms->heard, 0, vmu->maxmsg * sizeof(int));
4972 return 0;
4975 /* In Greek even though we CAN use a syntax like "friends messages"
4976 * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
4977 * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
4978 * syntax for the above three categories which is more elegant.
4981 static int vm_play_folder_name_gr(struct ast_channel *chan, char *mbox)
4983 int cmd;
4984 char *buf;
4986 buf = alloca(strlen(mbox)+2);
4987 strcpy(buf, mbox);
4988 strcat(buf,"s");
4990 if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")){
4991 cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
4992 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
4993 } else {
4994 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
4995 return cmd ? cmd : ast_play_and_wait(chan, mbox); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
4999 static int vm_play_folder_name_pl(struct ast_channel *chan, char *mbox)
5001 int cmd;
5003 if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")) {
5004 if (!strcasecmp(mbox, "vm-INBOX"))
5005 cmd = ast_play_and_wait(chan, "vm-new-e");
5006 else
5007 cmd = ast_play_and_wait(chan, "vm-old-e");
5008 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
5009 } else {
5010 cmd = ast_play_and_wait(chan, "vm-messages");
5011 return cmd ? cmd : ast_play_and_wait(chan, mbox);
5015 static int vm_play_folder_name_ua(struct ast_channel *chan, char *mbox)
5017 int cmd;
5019 if (!strcasecmp(mbox, "vm-Family") || !strcasecmp(mbox, "vm-Friends") || !strcasecmp(mbox, "vm-Work")){
5020 cmd = ast_play_and_wait(chan, "vm-messages");
5021 return cmd ? cmd : ast_play_and_wait(chan, mbox);
5022 } else {
5023 cmd = ast_play_and_wait(chan, mbox);
5024 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
5028 static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
5030 int cmd;
5032 if (!strcasecmp(chan->language, "it") || !strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* Italian, Spanish, French or Portuguese syntax */
5033 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
5034 return cmd ? cmd : ast_play_and_wait(chan, mbox);
5035 } else if (!strcasecmp(chan->language, "gr")){
5036 return vm_play_folder_name_gr(chan, mbox);
5037 } else if (!strcasecmp(chan->language, "pl")){
5038 return vm_play_folder_name_pl(chan, mbox);
5039 } else if (!strcasecmp(chan->language, "ua")){ /* Ukrainian syntax */
5040 return vm_play_folder_name_ua(chan, mbox);
5041 } else { /* Default English */
5042 cmd = ast_play_and_wait(chan, mbox);
5043 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
5047 /* GREEK SYNTAX
5048 In greek the plural for old/new is
5049 different so we need the following files
5050 We also need vm-denExeteMynhmata because
5051 this syntax is different.
5053 -> vm-Olds.wav : "Palia"
5054 -> vm-INBOXs.wav : "Nea"
5055 -> vm-denExeteMynhmata : "den exete mynhmata"
5059 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
5061 int res = 0;
5063 if (vms->newmessages) {
5064 res = ast_play_and_wait(chan, "vm-youhave");
5065 if (!res)
5066 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
5067 if (!res) {
5068 if ((vms->newmessages == 1)) {
5069 res = ast_play_and_wait(chan, "vm-INBOX");
5070 if (!res)
5071 res = ast_play_and_wait(chan, "vm-message");
5072 } else {
5073 res = ast_play_and_wait(chan, "vm-INBOXs");
5074 if (!res)
5075 res = ast_play_and_wait(chan, "vm-messages");
5078 } else if (vms->oldmessages){
5079 res = ast_play_and_wait(chan, "vm-youhave");
5080 if (!res)
5081 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
5082 if ((vms->oldmessages == 1)){
5083 res = ast_play_and_wait(chan, "vm-Old");
5084 if (!res)
5085 res = ast_play_and_wait(chan, "vm-message");
5086 } else {
5087 res = ast_play_and_wait(chan, "vm-Olds");
5088 if (!res)
5089 res = ast_play_and_wait(chan, "vm-messages");
5091 } else if (!vms->oldmessages && !vms->newmessages)
5092 res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
5093 return res;
5096 /* Default English syntax */
5097 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
5099 int res;
5101 /* Introduce messages they have */
5102 res = ast_play_and_wait(chan, "vm-youhave");
5103 if (!res) {
5104 if (vms->newmessages) {
5105 res = say_and_wait(chan, vms->newmessages, chan->language);
5106 if (!res)
5107 res = ast_play_and_wait(chan, "vm-INBOX");
5108 if (vms->oldmessages && !res)
5109 res = ast_play_and_wait(chan, "vm-and");
5110 else if (!res) {
5111 if ((vms->newmessages == 1))
5112 res = ast_play_and_wait(chan, "vm-message");
5113 else
5114 res = ast_play_and_wait(chan, "vm-messages");
5118 if (!res && vms->oldmessages) {
5119 res = say_and_wait(chan, vms->oldmessages, chan->language);
5120 if (!res)
5121 res = ast_play_and_wait(chan, "vm-Old");
5122 if (!res) {
5123 if (vms->oldmessages == 1)
5124 res = ast_play_and_wait(chan, "vm-message");
5125 else
5126 res = ast_play_and_wait(chan, "vm-messages");
5129 if (!res) {
5130 if (!vms->oldmessages && !vms->newmessages) {
5131 res = ast_play_and_wait(chan, "vm-no");
5132 if (!res)
5133 res = ast_play_and_wait(chan, "vm-messages");
5137 return res;
5140 /* ITALIAN syntax */
5141 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
5143 /* Introduce messages they have */
5144 int res;
5145 if (!vms->oldmessages && !vms->newmessages)
5146 res = ast_play_and_wait(chan, "vm-no") ||
5147 ast_play_and_wait(chan, "vm-message");
5148 else
5149 res = ast_play_and_wait(chan, "vm-youhave");
5150 if (!res && vms->newmessages) {
5151 res = (vms->newmessages == 1) ?
5152 ast_play_and_wait(chan, "digits/un") ||
5153 ast_play_and_wait(chan, "vm-nuovo") ||
5154 ast_play_and_wait(chan, "vm-message") :
5155 /* 2 or more new messages */
5156 say_and_wait(chan, vms->newmessages, chan->language) ||
5157 ast_play_and_wait(chan, "vm-nuovi") ||
5158 ast_play_and_wait(chan, "vm-messages");
5159 if (!res && vms->oldmessages)
5160 res = ast_play_and_wait(chan, "vm-and");
5162 if (!res && vms->oldmessages) {
5163 res = (vms->oldmessages == 1) ?
5164 ast_play_and_wait(chan, "digits/un") ||
5165 ast_play_and_wait(chan, "vm-vecchio") ||
5166 ast_play_and_wait(chan, "vm-message") :
5167 /* 2 or more old messages */
5168 say_and_wait(chan, vms->oldmessages, chan->language) ||
5169 ast_play_and_wait(chan, "vm-vecchi") ||
5170 ast_play_and_wait(chan, "vm-messages");
5172 return res ? -1 : 0;
5175 /* POLISH syntax */
5176 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
5178 /* Introduce messages they have */
5179 int res;
5180 div_t num;
5182 if (!vms->oldmessages && !vms->newmessages) {
5183 res = ast_play_and_wait(chan, "vm-no");
5184 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5185 return res;
5186 } else {
5187 res = ast_play_and_wait(chan, "vm-youhave");
5190 if (vms->newmessages) {
5191 num = div(vms->newmessages, 10);
5192 if (vms->newmessages == 1) {
5193 res = ast_play_and_wait(chan, "digits/1-a");
5194 res = res ? res : ast_play_and_wait(chan, "vm-new-a");
5195 res = res ? res : ast_play_and_wait(chan, "vm-message");
5196 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
5197 if (num.rem == 2) {
5198 if (!num.quot) {
5199 res = ast_play_and_wait(chan, "digits/2-ie");
5200 } else {
5201 res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
5202 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
5204 } else {
5205 res = say_and_wait(chan, vms->newmessages, chan->language);
5207 res = res ? res : ast_play_and_wait(chan, "vm-new-e");
5208 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5209 } else {
5210 res = say_and_wait(chan, vms->newmessages, chan->language);
5211 res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
5212 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5214 if (!res && vms->oldmessages)
5215 res = ast_play_and_wait(chan, "vm-and");
5217 if (!res && vms->oldmessages) {
5218 num = div(vms->oldmessages, 10);
5219 if (vms->oldmessages == 1) {
5220 res = ast_play_and_wait(chan, "digits/1-a");
5221 res = res ? res : ast_play_and_wait(chan, "vm-old-a");
5222 res = res ? res : ast_play_and_wait(chan, "vm-message");
5223 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
5224 if (num.rem == 2) {
5225 if (!num.quot) {
5226 res = ast_play_and_wait(chan, "digits/2-ie");
5227 } else {
5228 res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
5229 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
5231 } else {
5232 res = say_and_wait(chan, vms->oldmessages, chan->language);
5234 res = res ? res : ast_play_and_wait(chan, "vm-old-e");
5235 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5236 } else {
5237 res = say_and_wait(chan, vms->oldmessages, chan->language);
5238 res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
5239 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5243 return res;
5246 /* SWEDISH syntax */
5247 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
5249 /* Introduce messages they have */
5250 int res;
5252 res = ast_play_and_wait(chan, "vm-youhave");
5253 if (res)
5254 return res;
5256 if (!vms->oldmessages && !vms->newmessages) {
5257 res = ast_play_and_wait(chan, "vm-no");
5258 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5259 return res;
5262 if (vms->newmessages) {
5263 if ((vms->newmessages == 1)) {
5264 res = ast_play_and_wait(chan, "digits/ett");
5265 res = res ? res : ast_play_and_wait(chan, "vm-nytt");
5266 res = res ? res : ast_play_and_wait(chan, "vm-message");
5267 } else {
5268 res = say_and_wait(chan, vms->newmessages, chan->language);
5269 res = res ? res : ast_play_and_wait(chan, "vm-nya");
5270 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5272 if (!res && vms->oldmessages)
5273 res = ast_play_and_wait(chan, "vm-and");
5275 if (!res && vms->oldmessages) {
5276 if (vms->oldmessages == 1) {
5277 res = ast_play_and_wait(chan, "digits/ett");
5278 res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
5279 res = res ? res : ast_play_and_wait(chan, "vm-message");
5280 } else {
5281 res = say_and_wait(chan, vms->oldmessages, chan->language);
5282 res = res ? res : ast_play_and_wait(chan, "vm-gamla");
5283 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5287 return res;
5290 /* NORWEGIAN syntax */
5291 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
5293 /* Introduce messages they have */
5294 int res;
5296 res = ast_play_and_wait(chan, "vm-youhave");
5297 if (res)
5298 return res;
5300 if (!vms->oldmessages && !vms->newmessages) {
5301 res = ast_play_and_wait(chan, "vm-no");
5302 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5303 return res;
5306 if (vms->newmessages) {
5307 if ((vms->newmessages == 1)) {
5308 res = ast_play_and_wait(chan, "digits/1");
5309 res = res ? res : ast_play_and_wait(chan, "vm-ny");
5310 res = res ? res : ast_play_and_wait(chan, "vm-message");
5311 } else {
5312 res = say_and_wait(chan, vms->newmessages, chan->language);
5313 res = res ? res : ast_play_and_wait(chan, "vm-nye");
5314 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5316 if (!res && vms->oldmessages)
5317 res = ast_play_and_wait(chan, "vm-and");
5319 if (!res && vms->oldmessages) {
5320 if (vms->oldmessages == 1) {
5321 res = ast_play_and_wait(chan, "digits/1");
5322 res = res ? res : ast_play_and_wait(chan, "vm-gamel");
5323 res = res ? res : ast_play_and_wait(chan, "vm-message");
5324 } else {
5325 res = say_and_wait(chan, vms->oldmessages, chan->language);
5326 res = res ? res : ast_play_and_wait(chan, "vm-gamle");
5327 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5331 return res;
5334 /* GERMAN syntax */
5335 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
5337 /* Introduce messages they have */
5338 int res;
5339 res = ast_play_and_wait(chan, "vm-youhave");
5340 if (!res) {
5341 if (vms->newmessages) {
5342 if ((vms->newmessages == 1))
5343 res = ast_play_and_wait(chan, "digits/1F");
5344 else
5345 res = say_and_wait(chan, vms->newmessages, chan->language);
5346 if (!res)
5347 res = ast_play_and_wait(chan, "vm-INBOX");
5348 if (vms->oldmessages && !res)
5349 res = ast_play_and_wait(chan, "vm-and");
5350 else if (!res) {
5351 if ((vms->newmessages == 1))
5352 res = ast_play_and_wait(chan, "vm-message");
5353 else
5354 res = ast_play_and_wait(chan, "vm-messages");
5358 if (!res && vms->oldmessages) {
5359 if (vms->oldmessages == 1)
5360 res = ast_play_and_wait(chan, "digits/1F");
5361 else
5362 res = say_and_wait(chan, vms->oldmessages, chan->language);
5363 if (!res)
5364 res = ast_play_and_wait(chan, "vm-Old");
5365 if (!res) {
5366 if (vms->oldmessages == 1)
5367 res = ast_play_and_wait(chan, "vm-message");
5368 else
5369 res = ast_play_and_wait(chan, "vm-messages");
5372 if (!res) {
5373 if (!vms->oldmessages && !vms->newmessages) {
5374 res = ast_play_and_wait(chan, "vm-no");
5375 if (!res)
5376 res = ast_play_and_wait(chan, "vm-messages");
5380 return res;
5383 /* SPANISH syntax */
5384 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
5386 /* Introduce messages they have */
5387 int res;
5388 if (!vms->oldmessages && !vms->newmessages) {
5389 res = ast_play_and_wait(chan, "vm-youhaveno");
5390 if (!res)
5391 res = ast_play_and_wait(chan, "vm-messages");
5392 } else {
5393 res = ast_play_and_wait(chan, "vm-youhave");
5395 if (!res) {
5396 if (vms->newmessages) {
5397 if (!res) {
5398 if ((vms->newmessages == 1)) {
5399 res = ast_play_and_wait(chan, "digits/1M");
5400 if (!res)
5401 res = ast_play_and_wait(chan, "vm-message");
5402 if (!res)
5403 res = ast_play_and_wait(chan, "vm-INBOXs");
5404 } else {
5405 res = say_and_wait(chan, vms->newmessages, chan->language);
5406 if (!res)
5407 res = ast_play_and_wait(chan, "vm-messages");
5408 if (!res)
5409 res = ast_play_and_wait(chan, "vm-INBOX");
5412 if (vms->oldmessages && !res)
5413 res = ast_play_and_wait(chan, "vm-and");
5415 if (vms->oldmessages) {
5416 if (!res) {
5417 if (vms->oldmessages == 1) {
5418 res = ast_play_and_wait(chan, "digits/1M");
5419 if (!res)
5420 res = ast_play_and_wait(chan, "vm-message");
5421 if (!res)
5422 res = ast_play_and_wait(chan, "vm-Olds");
5423 } else {
5424 res = say_and_wait(chan, vms->oldmessages, chan->language);
5425 if (!res)
5426 res = ast_play_and_wait(chan, "vm-messages");
5427 if (!res)
5428 res = ast_play_and_wait(chan, "vm-Old");
5433 return res;
5436 /* BRAZILIAN PORTUGUESE syntax */
5437 static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
5438 /* Introduce messages they have */
5439 int res;
5440 if (!vms->oldmessages && !vms->newmessages) {
5441 res = ast_play_and_wait(chan, "vm-nomessages");
5442 return res;
5444 else {
5445 res = ast_play_and_wait(chan, "vm-youhave");
5447 if (vms->newmessages) {
5448 if (!res)
5449 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
5450 if ((vms->newmessages == 1)) {
5451 if (!res)
5452 res = ast_play_and_wait(chan, "vm-message");
5453 if (!res)
5454 res = ast_play_and_wait(chan, "vm-INBOXs");
5456 else {
5457 if (!res)
5458 res = ast_play_and_wait(chan, "vm-messages");
5459 if (!res)
5460 res = ast_play_and_wait(chan, "vm-INBOX");
5462 if (vms->oldmessages && !res)
5463 res = ast_play_and_wait(chan, "vm-and");
5465 if (vms->oldmessages) {
5466 if (!res)
5467 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
5468 if (vms->oldmessages == 1) {
5469 if (!res)
5470 res = ast_play_and_wait(chan, "vm-message");
5471 if (!res)
5472 res = ast_play_and_wait(chan, "vm-Olds");
5474 else {
5475 if (!res)
5476 res = ast_play_and_wait(chan, "vm-messages");
5477 if (!res)
5478 res = ast_play_and_wait(chan, "vm-Old");
5481 return res;
5484 /* FRENCH syntax */
5485 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
5487 /* Introduce messages they have */
5488 int res;
5489 res = ast_play_and_wait(chan, "vm-youhave");
5490 if (!res) {
5491 if (vms->newmessages) {
5492 res = say_and_wait(chan, vms->newmessages, chan->language);
5493 if (!res)
5494 res = ast_play_and_wait(chan, "vm-INBOX");
5495 if (vms->oldmessages && !res)
5496 res = ast_play_and_wait(chan, "vm-and");
5497 else if (!res) {
5498 if ((vms->newmessages == 1))
5499 res = ast_play_and_wait(chan, "vm-message");
5500 else
5501 res = ast_play_and_wait(chan, "vm-messages");
5505 if (!res && vms->oldmessages) {
5506 res = say_and_wait(chan, vms->oldmessages, chan->language);
5507 if (!res)
5508 res = ast_play_and_wait(chan, "vm-Old");
5509 if (!res) {
5510 if (vms->oldmessages == 1)
5511 res = ast_play_and_wait(chan, "vm-message");
5512 else
5513 res = ast_play_and_wait(chan, "vm-messages");
5516 if (!res) {
5517 if (!vms->oldmessages && !vms->newmessages) {
5518 res = ast_play_and_wait(chan, "vm-no");
5519 if (!res)
5520 res = ast_play_and_wait(chan, "vm-messages");
5524 return res;
5527 /* DUTCH syntax */
5528 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
5530 /* Introduce messages they have */
5531 int res;
5532 res = ast_play_and_wait(chan, "vm-youhave");
5533 if (!res) {
5534 if (vms->newmessages) {
5535 res = say_and_wait(chan, vms->newmessages, chan->language);
5536 if (!res) {
5537 if (vms->newmessages == 1)
5538 res = ast_play_and_wait(chan, "vm-INBOXs");
5539 else
5540 res = ast_play_and_wait(chan, "vm-INBOX");
5542 if (vms->oldmessages && !res)
5543 res = ast_play_and_wait(chan, "vm-and");
5544 else if (!res) {
5545 if ((vms->newmessages == 1))
5546 res = ast_play_and_wait(chan, "vm-message");
5547 else
5548 res = ast_play_and_wait(chan, "vm-messages");
5552 if (!res && vms->oldmessages) {
5553 res = say_and_wait(chan, vms->oldmessages, chan->language);
5554 if (!res) {
5555 if (vms->oldmessages == 1)
5556 res = ast_play_and_wait(chan, "vm-Olds");
5557 else
5558 res = ast_play_and_wait(chan, "vm-Old");
5560 if (!res) {
5561 if (vms->oldmessages == 1)
5562 res = ast_play_and_wait(chan, "vm-message");
5563 else
5564 res = ast_play_and_wait(chan, "vm-messages");
5567 if (!res) {
5568 if (!vms->oldmessages && !vms->newmessages) {
5569 res = ast_play_and_wait(chan, "vm-no");
5570 if (!res)
5571 res = ast_play_and_wait(chan, "vm-messages");
5575 return res;
5578 /* PORTUGUESE syntax */
5579 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
5581 /* Introduce messages they have */
5582 int res;
5583 res = ast_play_and_wait(chan, "vm-youhave");
5584 if (!res) {
5585 if (vms->newmessages) {
5586 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
5587 if (!res) {
5588 if ((vms->newmessages == 1)) {
5589 res = ast_play_and_wait(chan, "vm-message");
5590 if (!res)
5591 res = ast_play_and_wait(chan, "vm-INBOXs");
5592 } else {
5593 res = ast_play_and_wait(chan, "vm-messages");
5594 if (!res)
5595 res = ast_play_and_wait(chan, "vm-INBOX");
5598 if (vms->oldmessages && !res)
5599 res = ast_play_and_wait(chan, "vm-and");
5601 if (!res && vms->oldmessages) {
5602 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
5603 if (!res) {
5604 if (vms->oldmessages == 1) {
5605 res = ast_play_and_wait(chan, "vm-message");
5606 if (!res)
5607 res = ast_play_and_wait(chan, "vm-Olds");
5608 } else {
5609 res = ast_play_and_wait(chan, "vm-messages");
5610 if (!res)
5611 res = ast_play_and_wait(chan, "vm-Old");
5615 if (!res) {
5616 if (!vms->oldmessages && !vms->newmessages) {
5617 res = ast_play_and_wait(chan, "vm-no");
5618 if (!res)
5619 res = ast_play_and_wait(chan, "vm-messages");
5623 return res;
5627 /* CZECH syntax */
5628 /* in czech there must be declension of word new and message
5629 * czech : english : czech : english
5630 * --------------------------------------------------------
5631 * vm-youhave : you have
5632 * vm-novou : one new : vm-zpravu : message
5633 * vm-nove : 2-4 new : vm-zpravy : messages
5634 * vm-novych : 5-infinite new : vm-zprav : messages
5635 * vm-starou : one old
5636 * vm-stare : 2-4 old
5637 * vm-starych : 5-infinite old
5638 * jednu : one - falling 4.
5639 * vm-no : no ( no messages )
5642 static int vm_intro_cz(struct ast_channel *chan,struct vm_state *vms)
5644 int res;
5645 res = ast_play_and_wait(chan, "vm-youhave");
5646 if (!res) {
5647 if (vms->newmessages) {
5648 if (vms->newmessages == 1) {
5649 res = ast_play_and_wait(chan, "digits/jednu");
5650 } else {
5651 res = say_and_wait(chan, vms->newmessages, chan->language);
5653 if (!res) {
5654 if ((vms->newmessages == 1))
5655 res = ast_play_and_wait(chan, "vm-novou");
5656 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
5657 res = ast_play_and_wait(chan, "vm-nove");
5658 if (vms->newmessages > 4)
5659 res = ast_play_and_wait(chan, "vm-novych");
5661 if (vms->oldmessages && !res)
5662 res = ast_play_and_wait(chan, "vm-and");
5663 else if (!res) {
5664 if ((vms->newmessages == 1))
5665 res = ast_play_and_wait(chan, "vm-zpravu");
5666 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
5667 res = ast_play_and_wait(chan, "vm-zpravy");
5668 if (vms->newmessages > 4)
5669 res = ast_play_and_wait(chan, "vm-zprav");
5672 if (!res && vms->oldmessages) {
5673 res = say_and_wait(chan, vms->oldmessages, chan->language);
5674 if (!res) {
5675 if ((vms->oldmessages == 1))
5676 res = ast_play_and_wait(chan, "vm-starou");
5677 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
5678 res = ast_play_and_wait(chan, "vm-stare");
5679 if (vms->oldmessages > 4)
5680 res = ast_play_and_wait(chan, "vm-starych");
5682 if (!res) {
5683 if ((vms->oldmessages == 1))
5684 res = ast_play_and_wait(chan, "vm-zpravu");
5685 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
5686 res = ast_play_and_wait(chan, "vm-zpravy");
5687 if (vms->oldmessages > 4)
5688 res = ast_play_and_wait(chan, "vm-zprav");
5691 if (!res) {
5692 if (!vms->oldmessages && !vms->newmessages) {
5693 res = ast_play_and_wait(chan, "vm-no");
5694 if (!res)
5695 res = ast_play_and_wait(chan, "vm-zpravy");
5699 return res;
5702 static int get_lastdigits(int num)
5704 num %= 100;
5705 return (num < 20) ? num : num % 10;
5708 static int vm_intro_ru(struct ast_channel *chan,struct vm_state *vms)
5710 int res;
5711 int lastnum = 0;
5712 int dcnum;
5714 res = ast_play_and_wait(chan, "vm-youhave");
5715 if (!res && vms->newmessages) {
5716 lastnum = get_lastdigits(vms->newmessages);
5717 dcnum = vms->newmessages - lastnum;
5718 if (dcnum)
5719 res = say_and_wait(chan, dcnum, chan->language);
5720 if (!res && lastnum) {
5721 if (lastnum == 1)
5722 res = ast_play_and_wait(chan, "digits/odno");
5723 else
5724 res = say_and_wait(chan, lastnum, chan->language);
5727 if (!res)
5728 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-novoe" : "vm-novyh");
5730 if (!res && vms->oldmessages)
5731 res = ast_play_and_wait(chan, "vm-and");
5734 if (!res && vms->oldmessages) {
5735 lastnum = get_lastdigits(vms->oldmessages);
5736 dcnum = vms->oldmessages - lastnum;
5737 if (dcnum)
5738 res = say_and_wait(chan, dcnum, chan->language);
5739 if (!res && lastnum) {
5740 if (lastnum == 1)
5741 res = ast_play_and_wait(chan, "digits/odno");
5742 else
5743 res = say_and_wait(chan, lastnum, chan->language);
5746 if (!res)
5747 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-staroe" : "vm-staryh");
5750 if (!res && !vms->newmessages && !vms->oldmessages) {
5751 lastnum = 0;
5752 res = ast_play_and_wait(chan, "vm-no");
5755 if (!res) {
5756 switch (lastnum) {
5757 case 1:
5758 res = ast_play_and_wait(chan, "vm-soobshenie");
5759 break;
5760 case 2:
5761 case 3:
5762 case 4:
5763 res = ast_play_and_wait(chan, "vm-soobsheniya");
5764 break;
5765 default:
5766 res = ast_play_and_wait(chan, "vm-soobsheniy");
5767 break;
5771 return res;
5774 /* UKRAINIAN syntax */
5775 /* in ukrainian the syntax is different so we need the following files
5776 * --------------------------------------------------------
5777 * /digits/ua/1e 'odne'
5778 * vm-nove 'nove'
5779 * vm-stare 'stare'
5782 static int vm_intro_ua(struct ast_channel *chan,struct vm_state *vms)
5784 int res;
5785 int lastnum = 0;
5786 int dcnum;
5788 res = ast_play_and_wait(chan, "vm-youhave");
5789 if (!res && vms->newmessages) {
5790 lastnum = get_lastdigits(vms->newmessages);
5791 dcnum = vms->newmessages - lastnum;
5792 if (dcnum)
5793 res = say_and_wait(chan, dcnum, chan->language);
5794 if (!res && lastnum) {
5795 if (lastnum == 1)
5796 res = ast_play_and_wait(chan, "digits/ua/1e");
5797 else
5798 res = say_and_wait(chan, lastnum, chan->language);
5801 if (!res)
5802 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-nove" : "vm-INBOX");
5804 if (!res && vms->oldmessages)
5805 res = ast_play_and_wait(chan, "vm-and");
5808 if (!res && vms->oldmessages) {
5809 lastnum = get_lastdigits(vms->oldmessages);
5810 dcnum = vms->oldmessages - lastnum;
5811 if (dcnum)
5812 res = say_and_wait(chan, dcnum, chan->language);
5813 if (!res && lastnum) {
5814 if (lastnum == 1)
5815 res = ast_play_and_wait(chan, "digits/ua/1e");
5816 else
5817 res = say_and_wait(chan, lastnum, chan->language);
5820 if (!res)
5821 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-stare" : "vm-Old");
5824 if (!res && !vms->newmessages && !vms->oldmessages) {
5825 lastnum = 0;
5826 res = ast_play_and_wait(chan, "vm-no");
5829 if (!res) {
5830 switch (lastnum) {
5831 case 1:
5832 case 2:
5833 case 3:
5834 case 4:
5835 res = ast_play_and_wait(chan, "vm-message");
5836 break;
5837 default:
5838 res = ast_play_and_wait(chan, "vm-messages");
5839 break;
5843 return res;
5847 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
5849 char prefile[256];
5851 /* Notify the user that the temp greeting is set and give them the option to remove it */
5852 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
5853 if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
5854 if (ast_fileexists(prefile, NULL, NULL) > 0)
5855 ast_play_and_wait(chan, "vm-tempgreetactive");
5858 /* Play voicemail intro - syntax is different for different languages */
5859 if (!strcasecmp(chan->language, "de")) { /* GERMAN syntax */
5860 return vm_intro_de(chan, vms);
5861 } else if (!strcasecmp(chan->language, "es")) { /* SPANISH syntax */
5862 return vm_intro_es(chan, vms);
5863 } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN syntax */
5864 return vm_intro_it(chan, vms);
5865 } else if (!strcasecmp(chan->language, "fr")) { /* FRENCH syntax */
5866 return vm_intro_fr(chan, vms);
5867 } else if (!strcasecmp(chan->language, "nl")) { /* DUTCH syntax */
5868 return vm_intro_nl(chan, vms);
5869 } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE syntax */
5870 return vm_intro_pt(chan, vms);
5871 } else if (!strcasecmp(chan->language, "pt_BR")) { /* BRAZILIAN PORTUGUESE syntax */
5872 return vm_intro_pt_BR(chan, vms);
5873 } else if (!strcasecmp(chan->language, "cz")) { /* CZECH syntax */
5874 return vm_intro_cz(chan, vms);
5875 } else if (!strcasecmp(chan->language, "gr")) { /* GREEK syntax */
5876 return vm_intro_gr(chan, vms);
5877 } else if (!strcasecmp(chan->language, "pl")) { /* POLISH syntax */
5878 return vm_intro_pl(chan, vms);
5879 } else if (!strcasecmp(chan->language, "se")) { /* SWEDISH syntax */
5880 return vm_intro_se(chan, vms);
5881 } else if (!strcasecmp(chan->language, "no")) { /* NORWEGIAN syntax */
5882 return vm_intro_no(chan, vms);
5883 } else if (!strcasecmp(chan->language, "ru")) { /* RUSSIAN syntax */
5884 return vm_intro_ru(chan, vms);
5885 } else if (!strcasecmp(chan->language, "ua")) { /* UKRAINIAN syntax */
5886 return vm_intro_ua(chan, vms);
5887 } else { /* Default to ENGLISH */
5888 return vm_intro_en(chan, vms);
5892 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
5894 int res = 0;
5895 /* Play instructions and wait for new command */
5896 while (!res) {
5897 if (vms->starting) {
5898 if (vms->lastmsg > -1) {
5899 res = ast_play_and_wait(chan, "vm-onefor");
5900 if (!res)
5901 res = vm_play_folder_name(chan, vms->vmbox);
5903 if (!res)
5904 res = ast_play_and_wait(chan, "vm-opts");
5905 } else {
5906 if (vms->curmsg)
5907 res = ast_play_and_wait(chan, "vm-prev");
5908 if (!res && !skipadvanced)
5909 res = ast_play_and_wait(chan, "vm-advopts");
5910 if (!res)
5911 res = ast_play_and_wait(chan, "vm-repeat");
5912 if (!res && (vms->curmsg != vms->lastmsg))
5913 res = ast_play_and_wait(chan, "vm-next");
5914 if (!res) {
5915 if (!vms->deleted[vms->curmsg])
5916 res = ast_play_and_wait(chan, "vm-delete");
5917 else
5918 res = ast_play_and_wait(chan, "vm-undelete");
5919 if (!res)
5920 res = ast_play_and_wait(chan, "vm-toforward");
5921 if (!res)
5922 res = ast_play_and_wait(chan, "vm-savemessage");
5925 if (!res)
5926 res = ast_play_and_wait(chan, "vm-helpexit");
5927 if (!res)
5928 res = ast_waitfordigit(chan, 6000);
5929 if (!res) {
5930 vms->repeats++;
5931 if (vms->repeats > 2) {
5932 res = 't';
5936 return res;
5939 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
5941 int cmd = 0;
5942 int duration = 0;
5943 int tries = 0;
5944 char newpassword[80] = "";
5945 char newpassword2[80] = "";
5946 char prefile[PATH_MAX] = "";
5947 unsigned char buf[256];
5948 int bytes=0;
5950 if (ast_adsi_available(chan)) {
5951 bytes += adsi_logo(buf + bytes);
5952 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
5953 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
5954 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5955 bytes += ast_adsi_voice_mode(buf + bytes, 0);
5956 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5959 /* First, have the user change their password
5960 so they won't get here again */
5961 for (;;) {
5962 newpassword[1] = '\0';
5963 newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
5964 if (cmd == '#')
5965 newpassword[0] = '\0';
5966 if (cmd < 0 || cmd == 't' || cmd == '#')
5967 return cmd;
5968 cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
5969 if (cmd < 0 || cmd == 't' || cmd == '#')
5970 return cmd;
5971 newpassword2[1] = '\0';
5972 newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
5973 if (cmd == '#')
5974 newpassword2[0] = '\0';
5975 if (cmd < 0 || cmd == 't' || cmd == '#')
5976 return cmd;
5977 cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#");
5978 if (cmd < 0 || cmd == 't' || cmd == '#')
5979 return cmd;
5980 if (!strcmp(newpassword, newpassword2))
5981 break;
5982 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
5983 cmd = ast_play_and_wait(chan, "vm-mismatch");
5984 if (++tries == 3)
5985 return -1;
5987 if (ast_strlen_zero(ext_pass_cmd))
5988 vm_change_password(vmu,newpassword);
5989 else
5990 vm_change_password_shell(vmu,newpassword);
5991 if (option_debug > 2)
5992 ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
5993 cmd = ast_play_and_wait(chan,"vm-passchanged");
5995 /* If forcename is set, have the user record their name */
5996 if (ast_test_flag(vmu, VM_FORCENAME)) {
5997 snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
5998 if (ast_fileexists(prefile, NULL, NULL) < 1) {
5999 #ifndef IMAP_STORAGE
6000 cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6001 #else
6002 cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6003 #endif
6004 if (cmd < 0 || cmd == 't' || cmd == '#')
6005 return cmd;
6009 /* If forcegreetings is set, have the user record their greetings */
6010 if (ast_test_flag(vmu, VM_FORCEGREET)) {
6011 snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
6012 if (ast_fileexists(prefile, NULL, NULL) < 1) {
6013 #ifndef IMAP_STORAGE
6014 cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6015 #else
6016 cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6017 #endif
6018 if (cmd < 0 || cmd == 't' || cmd == '#')
6019 return cmd;
6022 snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
6023 if (ast_fileexists(prefile, NULL, NULL) < 1) {
6024 #ifndef IMAP_STORAGE
6025 cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6026 #else
6027 cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6028 #endif
6029 if (cmd < 0 || cmd == 't' || cmd == '#')
6030 return cmd;
6034 return cmd;
6037 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
6039 int cmd = 0;
6040 int retries = 0;
6041 int duration = 0;
6042 char newpassword[80] = "";
6043 char newpassword2[80] = "";
6044 char prefile[PATH_MAX] = "";
6045 unsigned char buf[256];
6046 int bytes=0;
6048 if (ast_adsi_available(chan))
6050 bytes += adsi_logo(buf + bytes);
6051 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
6052 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
6053 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6054 bytes += ast_adsi_voice_mode(buf + bytes, 0);
6055 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6057 while ((cmd >= 0) && (cmd != 't')) {
6058 if (cmd)
6059 retries = 0;
6060 switch (cmd) {
6061 case '1':
6062 snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
6063 #ifndef IMAP_STORAGE
6064 cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6065 #else
6066 cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6067 #endif
6068 break;
6069 case '2':
6070 snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
6071 #ifndef IMAP_STORAGE
6072 cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6073 #else
6074 cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6075 #endif
6076 break;
6077 case '3':
6078 snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
6079 #ifndef IMAP_STORAGE
6080 cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6081 #else
6082 cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6083 #endif
6084 break;
6085 case '4':
6086 cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
6087 break;
6088 case '5':
6089 if (vmu->password[0] == '-') {
6090 cmd = ast_play_and_wait(chan, "vm-no");
6091 break;
6093 newpassword[1] = '\0';
6094 newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
6095 if (cmd == '#')
6096 newpassword[0] = '\0';
6097 else {
6098 if (cmd < 0)
6099 break;
6100 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
6101 break;
6104 newpassword2[1] = '\0';
6105 newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
6106 if (cmd == '#')
6107 newpassword2[0] = '\0';
6108 else {
6109 if (cmd < 0)
6110 break;
6112 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
6113 break;
6116 if (strcmp(newpassword, newpassword2)) {
6117 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
6118 cmd = ast_play_and_wait(chan, "vm-mismatch");
6119 break;
6121 if (ast_strlen_zero(ext_pass_cmd))
6122 vm_change_password(vmu,newpassword);
6123 else
6124 vm_change_password_shell(vmu,newpassword);
6125 if (option_debug > 2)
6126 ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
6127 cmd = ast_play_and_wait(chan,"vm-passchanged");
6128 break;
6129 case '*':
6130 cmd = 't';
6131 break;
6132 default:
6133 cmd = 0;
6134 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
6135 if (ast_fileexists(prefile, NULL, NULL))
6136 cmd = ast_play_and_wait(chan, "vm-tmpexists");
6137 if (!cmd)
6138 cmd = ast_play_and_wait(chan, "vm-options");
6139 if (!cmd)
6140 cmd = ast_waitfordigit(chan,6000);
6141 if (!cmd)
6142 retries++;
6143 if (retries > 3)
6144 cmd = 't';
6147 if (cmd == 't')
6148 cmd = 0;
6149 return cmd;
6152 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
6154 int res;
6155 int cmd = 0;
6156 int retries = 0;
6157 int duration = 0;
6158 char prefile[PATH_MAX] = "";
6159 unsigned char buf[256];
6160 char dest[PATH_MAX];
6161 int bytes = 0;
6163 if (ast_adsi_available(chan)) {
6164 bytes += adsi_logo(buf + bytes);
6165 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
6166 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
6167 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6168 bytes += ast_adsi_voice_mode(buf + bytes, 0);
6169 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6172 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
6173 if ((res = create_dirpath(dest, sizeof(dest), vmu->context, vms->username, "temp"))) {
6174 ast_log(LOG_WARNING, "Failed to create directory (%s).\n", prefile);
6175 return -1;
6177 while ((cmd >= 0) && (cmd != 't')) {
6178 if (cmd)
6179 retries = 0;
6180 RETRIEVE(prefile, -1);
6181 if (ast_fileexists(prefile, NULL, NULL) <= 0) {
6182 #ifndef IMAP_STORAGE
6183 play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6184 #else
6185 play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6186 #endif
6187 cmd = 't';
6188 } else {
6189 switch (cmd) {
6190 case '1':
6191 #ifndef IMAP_STORAGE
6192 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
6193 #else
6194 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6195 #endif
6196 break;
6197 case '2':
6198 DELETE(prefile, -1, prefile);
6199 ast_play_and_wait(chan, "vm-tempremoved");
6200 cmd = 't';
6201 break;
6202 case '*':
6203 cmd = 't';
6204 break;
6205 default:
6206 cmd = ast_play_and_wait(chan,
6207 ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
6208 "vm-tempgreeting2" : "vm-tempgreeting");
6209 if (!cmd)
6210 cmd = ast_waitfordigit(chan,6000);
6211 if (!cmd)
6212 retries++;
6213 if (retries > 3)
6214 cmd = 't';
6217 DISPOSE(prefile, -1);
6219 if (cmd == 't')
6220 cmd = 0;
6221 return cmd;
6224 /* GREEK SYNTAX */
6226 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6228 int cmd=0;
6230 if (vms->lastmsg > -1) {
6231 cmd = play_message(chan, vmu, vms);
6232 } else {
6233 cmd = ast_play_and_wait(chan, "vm-youhaveno");
6234 if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
6235 if (!cmd) {
6236 snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
6237 cmd = ast_play_and_wait(chan, vms->fn);
6239 if (!cmd)
6240 cmd = ast_play_and_wait(chan, "vm-messages");
6241 } else {
6242 if (!cmd)
6243 cmd = ast_play_and_wait(chan, "vm-messages");
6244 if (!cmd) {
6245 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6246 cmd = ast_play_and_wait(chan, vms->fn);
6250 return cmd;
6253 /* Default English syntax */
6254 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6256 int cmd=0;
6258 if (vms->lastmsg > -1) {
6259 cmd = play_message(chan, vmu, vms);
6260 } else {
6261 cmd = ast_play_and_wait(chan, "vm-youhave");
6262 if (!cmd)
6263 cmd = ast_play_and_wait(chan, "vm-no");
6264 if (!cmd) {
6265 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6266 cmd = ast_play_and_wait(chan, vms->fn);
6268 if (!cmd)
6269 cmd = ast_play_and_wait(chan, "vm-messages");
6271 return cmd;
6274 /* ITALIAN syntax */
6275 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6277 int cmd=0;
6279 if (vms->lastmsg > -1) {
6280 cmd = play_message(chan, vmu, vms);
6281 } else {
6282 cmd = ast_play_and_wait(chan, "vm-no");
6283 if (!cmd)
6284 cmd = ast_play_and_wait(chan, "vm-message");
6285 if (!cmd) {
6286 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6287 cmd = ast_play_and_wait(chan, vms->fn);
6290 return cmd;
6293 /* SPANISH syntax */
6294 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6296 int cmd=0;
6298 if (vms->lastmsg > -1) {
6299 cmd = play_message(chan, vmu, vms);
6300 } else {
6301 cmd = ast_play_and_wait(chan, "vm-youhaveno");
6302 if (!cmd)
6303 cmd = ast_play_and_wait(chan, "vm-messages");
6304 if (!cmd) {
6305 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6306 cmd = ast_play_and_wait(chan, vms->fn);
6309 return cmd;
6312 /* PORTUGUESE syntax */
6313 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6315 int cmd=0;
6317 if (vms->lastmsg > -1) {
6318 cmd = play_message(chan, vmu, vms);
6319 } else {
6320 cmd = ast_play_and_wait(chan, "vm-no");
6321 if (!cmd) {
6322 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6323 cmd = ast_play_and_wait(chan, vms->fn);
6325 if (!cmd)
6326 cmd = ast_play_and_wait(chan, "vm-messages");
6328 return cmd;
6331 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6333 if (!strcasecmp(chan->language, "es")) { /* SPANISH */
6334 return vm_browse_messages_es(chan, vms, vmu);
6335 } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN */
6336 return vm_browse_messages_it(chan, vms, vmu);
6337 } else if (!strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* PORTUGUESE */
6338 return vm_browse_messages_pt(chan, vms, vmu);
6339 } else if (!strcasecmp(chan->language, "gr")){
6340 return vm_browse_messages_gr(chan, vms, vmu); /* GREEK */
6341 } else { /* Default to English syntax */
6342 return vm_browse_messages_en(chan, vms, vmu);
6346 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
6347 struct ast_vm_user *res_vmu, const char *context, const char *prefix,
6348 int skipuser, int maxlogins, int silent)
6350 int useadsi=0, valid=0, logretries=0;
6351 char password[AST_MAX_EXTENSION]="", *passptr;
6352 struct ast_vm_user vmus, *vmu = NULL;
6354 /* If ADSI is supported, setup login screen */
6355 adsi_begin(chan, &useadsi);
6356 if (!skipuser && useadsi)
6357 adsi_login(chan);
6358 if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
6359 ast_log(LOG_WARNING, "Couldn't stream login file\n");
6360 return -1;
6363 /* Authenticate them and get their mailbox/password */
6365 while (!valid && (logretries < maxlogins)) {
6366 /* Prompt for, and read in the username */
6367 if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
6368 ast_log(LOG_WARNING, "Couldn't read username\n");
6369 return -1;
6371 if (ast_strlen_zero(mailbox)) {
6372 if (chan->cid.cid_num) {
6373 ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
6374 } else {
6375 if (option_verbose > 2)
6376 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
6377 return -1;
6380 if (useadsi)
6381 adsi_password(chan);
6383 if (!ast_strlen_zero(prefix)) {
6384 char fullusername[80] = "";
6385 ast_copy_string(fullusername, prefix, sizeof(fullusername));
6386 strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
6387 ast_copy_string(mailbox, fullusername, mailbox_size);
6390 if (option_debug)
6391 ast_log(LOG_DEBUG, "Before find user for mailbox %s\n",mailbox);
6392 vmu = find_user(&vmus, context, mailbox);
6393 if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
6394 /* saved password is blank, so don't bother asking */
6395 password[0] = '\0';
6396 } else {
6397 if (ast_streamfile(chan, "vm-password", chan->language)) {
6398 ast_log(LOG_WARNING, "Unable to stream password file\n");
6399 return -1;
6401 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
6402 ast_log(LOG_WARNING, "Unable to read password\n");
6403 return -1;
6407 if (vmu) {
6408 passptr = vmu->password;
6409 if (passptr[0] == '-') passptr++;
6411 if (vmu && !strcmp(passptr, password))
6412 valid++;
6413 else {
6414 if (option_verbose > 2)
6415 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
6416 if (!ast_strlen_zero(prefix))
6417 mailbox[0] = '\0';
6419 logretries++;
6420 if (!valid) {
6421 if (skipuser || logretries >= maxlogins) {
6422 if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
6423 ast_log(LOG_WARNING, "Unable to stream incorrect message\n");
6424 return -1;
6426 } else {
6427 if (useadsi)
6428 adsi_login(chan);
6429 if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
6430 ast_log(LOG_WARNING, "Unable to stream incorrect mailbox message\n");
6431 return -1;
6434 if (ast_waitstream(chan, "")) /* Channel is hung up */
6435 return -1;
6438 if (!valid && (logretries >= maxlogins)) {
6439 ast_stopstream(chan);
6440 ast_play_and_wait(chan, "vm-goodbye");
6441 return -1;
6443 if (vmu && !skipuser) {
6444 memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
6446 return 0;
6449 static int vm_execmain(struct ast_channel *chan, void *data)
6451 /* XXX This is, admittedly, some pretty horrendus code. For some
6452 reason it just seemed a lot easier to do with GOTO's. I feel
6453 like I'm back in my GWBASIC days. XXX */
6454 int res=-1;
6455 int cmd=0;
6456 int valid = 0;
6457 struct ast_module_user *u;
6458 char prefixstr[80] ="";
6459 char ext_context[256]="";
6460 int box;
6461 int useadsi = 0;
6462 int skipuser = 0;
6463 struct vm_state vms;
6464 struct ast_vm_user *vmu = NULL, vmus;
6465 char *context=NULL;
6466 int silentexit = 0;
6467 struct ast_flags flags = { 0 };
6468 signed char record_gain = 0;
6469 int play_auto = 0;
6470 int play_folder = 0;
6471 #ifdef IMAP_STORAGE
6472 int deleted = 0;
6473 #endif
6474 u = ast_module_user_add(chan);
6476 /* Add the vm_state to the active list and keep it active */
6477 memset(&vms, 0, sizeof(vms));
6478 vms.lastmsg = -1;
6480 memset(&vmus, 0, sizeof(vmus));
6482 if (chan->_state != AST_STATE_UP) {
6483 if (option_debug)
6484 ast_log(LOG_DEBUG, "Before ast_answer\n");
6485 ast_answer(chan);
6488 if (!ast_strlen_zero(data)) {
6489 char *opts[OPT_ARG_ARRAY_SIZE];
6490 char *parse;
6491 AST_DECLARE_APP_ARGS(args,
6492 AST_APP_ARG(argv0);
6493 AST_APP_ARG(argv1);
6496 parse = ast_strdupa(data);
6498 AST_STANDARD_APP_ARGS(args, parse);
6500 if (args.argc == 2) {
6501 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
6502 ast_module_user_remove(u);
6503 return -1;
6505 if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
6506 int gain;
6507 if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
6508 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
6509 ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
6510 ast_module_user_remove(u);
6511 return -1;
6512 } else {
6513 record_gain = (signed char) gain;
6515 } else {
6516 ast_log(LOG_WARNING, "Invalid Gain level set with option g\n");
6519 if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
6520 play_auto = 1;
6521 if (opts[OPT_ARG_PLAYFOLDER]) {
6522 if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%d", &play_folder) != 1) {
6523 ast_log(LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
6525 } else {
6526 ast_log(LOG_WARNING, "Invalid folder set with option a\n");
6528 if ( play_folder > 9 || play_folder < 0) {
6529 ast_log(LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
6530 play_folder = 0;
6533 } else {
6534 /* old style options parsing */
6535 while (*(args.argv0)) {
6536 if (*(args.argv0) == 's')
6537 ast_set_flag(&flags, OPT_SILENT);
6538 else if (*(args.argv0) == 'p')
6539 ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
6540 else
6541 break;
6542 (args.argv0)++;
6547 valid = ast_test_flag(&flags, OPT_SILENT);
6549 if ((context = strchr(args.argv0, '@')))
6550 *context++ = '\0';
6552 if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
6553 ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
6554 else
6555 ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
6557 if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
6558 skipuser++;
6559 else
6560 valid = 0;
6563 if (!valid)
6564 res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
6566 if (option_debug)
6567 ast_log(LOG_DEBUG, "After vm_authenticate\n");
6568 if (!res) {
6569 valid = 1;
6570 if (!skipuser)
6571 vmu = &vmus;
6572 } else {
6573 res = 0;
6576 /* If ADSI is supported, setup login screen */
6577 adsi_begin(chan, &useadsi);
6579 #ifdef IMAP_STORAGE
6580 vms.interactive = 1;
6581 vms.updated = 1;
6582 vmstate_insert(&vms);
6583 init_vm_state(&vms);
6584 #endif
6585 if (!valid)
6586 goto out;
6588 if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
6589 /* TODO: Handle memory allocation failure */
6591 if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
6592 /* TODO: Handle memory allocation failure */
6595 /* Set language from config to override channel language */
6596 if (!ast_strlen_zero(vmu->language))
6597 ast_string_field_set(chan, language, vmu->language);
6598 create_dirpath(vms.curdir, sizeof(vms.curdir), vmu->context, vms.username, "");
6599 /* Retrieve old and new message counts */
6600 if (option_debug)
6601 ast_log(LOG_DEBUG, "Before open_mailbox\n");
6602 res = open_mailbox(&vms, vmu, 1);
6603 if (res == ERROR_LOCK_PATH)
6604 goto out;
6605 vms.oldmessages = vms.lastmsg + 1;
6606 if (option_debug > 2)
6607 ast_log(LOG_DEBUG, "Number of old messages: %d\n",vms.oldmessages);
6608 /* Start in INBOX */
6609 res = open_mailbox(&vms, vmu, 0);
6610 if (res == ERROR_LOCK_PATH)
6611 goto out;
6612 vms.newmessages = vms.lastmsg + 1;
6613 if (option_debug > 2)
6614 ast_log(LOG_DEBUG, "Number of new messages: %d\n",vms.newmessages);
6616 /* Select proper mailbox FIRST!! */
6617 if (play_auto) {
6618 res = open_mailbox(&vms, vmu, play_folder);
6619 if (res == ERROR_LOCK_PATH)
6620 goto out;
6622 /* If there are no new messages, inform the user and hangup */
6623 if (vms.lastmsg == -1) {
6624 cmd = vm_browse_messages(chan, &vms, vmu);
6625 res = 0;
6626 goto out;
6628 } else {
6629 if (!vms.newmessages && vms.oldmessages) {
6630 /* If we only have old messages start here */
6631 res = open_mailbox(&vms, vmu, 1);
6632 play_folder = 1;
6633 if (res == ERROR_LOCK_PATH)
6634 goto out;
6638 if (useadsi)
6639 adsi_status(chan, &vms);
6640 res = 0;
6642 /* Check to see if this is a new user */
6643 if (!strcasecmp(vmu->mailbox, vmu->password) &&
6644 (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
6645 if (ast_play_and_wait(chan, "vm-newuser") == -1)
6646 ast_log(LOG_WARNING, "Couldn't stream new user file\n");
6647 cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
6648 if ((cmd == 't') || (cmd == '#')) {
6649 /* Timeout */
6650 res = 0;
6651 goto out;
6652 } else if (cmd < 0) {
6653 /* Hangup */
6654 res = -1;
6655 goto out;
6658 #ifdef IMAP_STORAGE
6659 if (option_debug > 2)
6660 ast_log(LOG_DEBUG, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
6661 if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
6662 if (option_debug)
6663 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!!\n");
6664 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
6666 if (option_debug > 2)
6667 ast_log(LOG_DEBUG, "Checking quotas: User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
6668 if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
6669 ast_log(LOG_WARNING, "No more messages possible. User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
6670 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
6672 #endif
6673 if (play_auto) {
6674 cmd = '1';
6675 } else {
6676 cmd = vm_intro(chan, vmu, &vms);
6679 vms.repeats = 0;
6680 vms.starting = 1;
6681 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
6682 /* Run main menu */
6683 switch (cmd) {
6684 case '1':
6685 vms.curmsg = 0;
6686 /* Fall through */
6687 case '5':
6688 cmd = vm_browse_messages(chan, &vms, vmu);
6689 break;
6690 case '2': /* Change folders */
6691 if (useadsi)
6692 adsi_folders(chan, 0, "Change to folder...");
6693 cmd = get_folder2(chan, "vm-changeto", 0);
6694 if (cmd == '#') {
6695 cmd = 0;
6696 } else if (cmd > 0) {
6697 cmd = cmd - '0';
6698 res = close_mailbox(&vms, vmu);
6699 if (res == ERROR_LOCK_PATH)
6700 goto out;
6701 res = open_mailbox(&vms, vmu, cmd);
6702 if (res == ERROR_LOCK_PATH)
6703 goto out;
6704 play_folder = cmd;
6705 cmd = 0;
6707 if (useadsi)
6708 adsi_status2(chan, &vms);
6710 if (!cmd)
6711 cmd = vm_play_folder_name(chan, vms.vmbox);
6713 vms.starting = 1;
6714 break;
6715 case '3': /* Advanced options */
6716 cmd = 0;
6717 vms.repeats = 0;
6718 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
6719 switch (cmd) {
6720 case '1': /* Reply */
6721 if (vms.lastmsg > -1 && !vms.starting) {
6722 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
6723 if (cmd == ERROR_LOCK_PATH) {
6724 res = cmd;
6725 goto out;
6727 } else
6728 cmd = ast_play_and_wait(chan, "vm-sorry");
6729 cmd = 't';
6730 break;
6731 case '2': /* Callback */
6732 if (option_verbose > 2 && !vms.starting)
6733 ast_verbose( VERBOSE_PREFIX_3 "Callback Requested\n");
6734 if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
6735 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
6736 if (cmd == 9) {
6737 silentexit = 1;
6738 goto out;
6739 } else if (cmd == ERROR_LOCK_PATH) {
6740 res = cmd;
6741 goto out;
6744 else
6745 cmd = ast_play_and_wait(chan, "vm-sorry");
6746 cmd = 't';
6747 break;
6748 case '3': /* Envelope */
6749 if (vms.lastmsg > -1 && !vms.starting) {
6750 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
6751 if (cmd == ERROR_LOCK_PATH) {
6752 res = cmd;
6753 goto out;
6755 } else
6756 cmd = ast_play_and_wait(chan, "vm-sorry");
6757 cmd = 't';
6758 break;
6759 case '4': /* Dialout */
6760 if (!ast_strlen_zero(vmu->dialout)) {
6761 cmd = dialout(chan, vmu, NULL, vmu->dialout);
6762 if (cmd == 9) {
6763 silentexit = 1;
6764 goto out;
6767 else
6768 cmd = ast_play_and_wait(chan, "vm-sorry");
6769 cmd = 't';
6770 break;
6772 case '5': /* Leave VoiceMail */
6773 if (ast_test_flag(vmu, VM_SVMAIL)) {
6774 cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain);
6775 if (cmd == ERROR_LOCK_PATH) {
6776 res = cmd;
6777 ast_log(LOG_WARNING, "forward_message failed to lock path.\n");
6778 goto out;
6780 } else
6781 cmd = ast_play_and_wait(chan,"vm-sorry");
6782 cmd='t';
6783 break;
6785 case '*': /* Return to main menu */
6786 cmd = 't';
6787 break;
6789 default:
6790 cmd = 0;
6791 if (!vms.starting) {
6792 cmd = ast_play_and_wait(chan, "vm-toreply");
6794 if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
6795 cmd = ast_play_and_wait(chan, "vm-tocallback");
6797 if (!cmd && !vms.starting) {
6798 cmd = ast_play_and_wait(chan, "vm-tohearenv");
6800 if (!ast_strlen_zero(vmu->dialout) && !cmd) {
6801 cmd = ast_play_and_wait(chan, "vm-tomakecall");
6803 if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
6804 cmd=ast_play_and_wait(chan, "vm-leavemsg");
6805 if (!cmd)
6806 cmd = ast_play_and_wait(chan, "vm-starmain");
6807 if (!cmd)
6808 cmd = ast_waitfordigit(chan,6000);
6809 if (!cmd)
6810 vms.repeats++;
6811 if (vms.repeats > 3)
6812 cmd = 't';
6815 if (cmd == 't') {
6816 cmd = 0;
6817 vms.repeats = 0;
6819 break;
6820 case '4':
6821 if (vms.curmsg > 0) {
6822 vms.curmsg--;
6823 cmd = play_message(chan, vmu, &vms);
6824 } else {
6825 cmd = ast_play_and_wait(chan, "vm-nomore");
6827 break;
6828 case '6':
6829 if (vms.curmsg < vms.lastmsg) {
6830 vms.curmsg++;
6831 cmd = play_message(chan, vmu, &vms);
6832 } else {
6833 cmd = ast_play_and_wait(chan, "vm-nomore");
6835 break;
6836 case '7':
6837 if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
6838 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
6839 if (useadsi)
6840 adsi_delete(chan, &vms);
6841 if (vms.deleted[vms.curmsg]) {
6842 if (play_folder == 0)
6843 vms.newmessages--;
6844 else if (play_folder == 1)
6845 vms.oldmessages--;
6846 cmd = ast_play_and_wait(chan, "vm-deleted");
6848 else {
6849 if (play_folder == 0)
6850 vms.newmessages++;
6851 else if (play_folder == 1)
6852 vms.oldmessages++;
6853 cmd = ast_play_and_wait(chan, "vm-undeleted");
6855 if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
6856 if (vms.curmsg < vms.lastmsg) {
6857 vms.curmsg++;
6858 cmd = play_message(chan, vmu, &vms);
6859 } else {
6860 cmd = ast_play_and_wait(chan, "vm-nomore");
6863 } else /* Delete not valid if we haven't selected a message */
6864 cmd = 0;
6865 #ifdef IMAP_STORAGE
6866 deleted = 1;
6867 #endif
6868 break;
6870 case '8':
6871 if (vms.lastmsg > -1) {
6872 cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain);
6873 if (cmd == ERROR_LOCK_PATH) {
6874 res = cmd;
6875 goto out;
6877 } else
6878 cmd = ast_play_and_wait(chan, "vm-nomore");
6879 break;
6880 case '9':
6881 if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
6882 /* No message selected */
6883 cmd = 0;
6884 break;
6886 if (useadsi)
6887 adsi_folders(chan, 1, "Save to folder...");
6888 cmd = get_folder2(chan, "vm-savefolder", 1);
6889 box = 0; /* Shut up compiler */
6890 if (cmd == '#') {
6891 cmd = 0;
6892 break;
6893 } else if (cmd > 0) {
6894 box = cmd = cmd - '0';
6895 cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
6896 if (cmd == ERROR_LOCK_PATH) {
6897 res = cmd;
6898 goto out;
6899 #ifdef IMAP_STORAGE
6900 } else if (cmd == 10) {
6901 goto out;
6902 #endif
6903 } else if (!cmd) {
6904 vms.deleted[vms.curmsg] = 1;
6905 } else {
6906 vms.deleted[vms.curmsg] = 0;
6907 vms.heard[vms.curmsg] = 0;
6910 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
6911 if (useadsi)
6912 adsi_message(chan, &vms);
6913 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
6914 if (!cmd) {
6915 cmd = ast_play_and_wait(chan, "vm-message");
6916 if (!cmd)
6917 cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
6918 if (!cmd)
6919 cmd = ast_play_and_wait(chan, "vm-savedto");
6920 if (!cmd)
6921 cmd = vm_play_folder_name(chan, vms.fn);
6922 } else {
6923 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
6925 if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
6926 if (vms.curmsg < vms.lastmsg) {
6927 vms.curmsg++;
6928 cmd = play_message(chan, vmu, &vms);
6929 } else {
6930 cmd = ast_play_and_wait(chan, "vm-nomore");
6933 break;
6934 case '*':
6935 if (!vms.starting) {
6936 cmd = ast_play_and_wait(chan, "vm-onefor");
6937 if (!cmd)
6938 cmd = vm_play_folder_name(chan, vms.vmbox);
6939 if (!cmd)
6940 cmd = ast_play_and_wait(chan, "vm-opts");
6941 if (!cmd)
6942 cmd = vm_instructions(chan, &vms, 1);
6943 } else
6944 cmd = 0;
6945 break;
6946 case '0':
6947 cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
6948 if (useadsi)
6949 adsi_status(chan, &vms);
6950 break;
6951 default: /* Nothing */
6952 cmd = vm_instructions(chan, &vms, 0);
6953 break;
6956 if ((cmd == 't') || (cmd == '#')) {
6957 /* Timeout */
6958 res = 0;
6959 } else {
6960 /* Hangup */
6961 res = -1;
6964 out:
6965 if (res > -1) {
6966 ast_stopstream(chan);
6967 adsi_goodbye(chan);
6968 if (valid) {
6969 if (silentexit)
6970 res = ast_play_and_wait(chan, "vm-dialout");
6971 else
6972 res = ast_play_and_wait(chan, "vm-goodbye");
6973 if (res > 0)
6974 res = 0;
6976 if (useadsi)
6977 ast_adsi_unload_session(chan);
6979 if (vmu)
6980 close_mailbox(&vms, vmu);
6981 if (valid) {
6982 snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
6983 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
6984 run_externnotify(vmu->context, vmu->mailbox);
6986 #ifdef IMAP_STORAGE
6987 /* expunge message - use UID Expunge if supported on IMAP server*/
6988 if (option_debug > 2)
6989 ast_log(LOG_DEBUG, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
6990 if (vmu && deleted == 1 && expungeonhangup == 1) {
6991 #ifdef HAVE_IMAP_TK2006
6992 if (LEVELUIDPLUS (vms.mailstream)) {
6993 mail_expunge_full(vms.mailstream,NIL,EX_UID);
6994 } else
6995 #endif
6996 mail_expunge(vms.mailstream);
6998 /* before we delete the state, we should copy pertinent info
6999 * back to the persistent model */
7000 vmstate_delete(&vms);
7001 #endif
7002 if (vmu)
7003 free_user(vmu);
7004 if (vms.deleted)
7005 free(vms.deleted);
7006 if (vms.heard)
7007 free(vms.heard);
7008 ast_module_user_remove(u);
7010 return res;
7013 static int vm_exec(struct ast_channel *chan, void *data)
7015 int res = 0;
7016 struct ast_module_user *u;
7017 char *tmp;
7018 struct leave_vm_options leave_options;
7019 struct ast_flags flags = { 0 };
7020 static int deprecate_warning = 0;
7021 char *opts[OPT_ARG_ARRAY_SIZE];
7022 AST_DECLARE_APP_ARGS(args,
7023 AST_APP_ARG(argv0);
7024 AST_APP_ARG(argv1);
7027 u = ast_module_user_add(chan);
7029 memset(&leave_options, 0, sizeof(leave_options));
7031 if (chan->_state != AST_STATE_UP)
7032 ast_answer(chan);
7034 if (!ast_strlen_zero(data)) {
7035 tmp = ast_strdupa(data);
7036 AST_STANDARD_APP_ARGS(args, tmp);
7037 if (args.argc == 2) {
7038 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
7039 ast_module_user_remove(u);
7040 return -1;
7042 ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_PRIORITY_JUMP);
7043 if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
7044 int gain;
7046 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
7047 ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
7048 ast_module_user_remove(u);
7049 return -1;
7050 } else {
7051 leave_options.record_gain = (signed char) gain;
7054 } else {
7055 /* old style options parsing */
7056 int old = 0;
7057 char *orig_argv0 = args.argv0;
7058 while (*(args.argv0)) {
7059 if (*(args.argv0) == 's') {
7060 old = 1;
7061 ast_set_flag(&leave_options, OPT_SILENT);
7062 } else if (*(args.argv0) == 'b') {
7063 old = 1;
7064 ast_set_flag(&leave_options, OPT_BUSY_GREETING);
7065 } else if (*(args.argv0) == 'u') {
7066 old = 1;
7067 ast_set_flag(&leave_options, OPT_UNAVAIL_GREETING);
7068 } else if (*(args.argv0) == 'j') {
7069 old = 1;
7070 ast_set_flag(&leave_options, OPT_PRIORITY_JUMP);
7071 } else
7072 break;
7073 (args.argv0)++;
7075 if (!deprecate_warning && old) {
7076 deprecate_warning = 1;
7077 ast_log(LOG_WARNING, "Prefixing the mailbox with an option is deprecated ('%s').\n", orig_argv0);
7078 ast_log(LOG_WARNING, "Please move all leading options to the second argument.\n");
7081 } else {
7082 char tmp[256];
7083 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
7084 if (res < 0) {
7085 ast_module_user_remove(u);
7086 return res;
7088 if (ast_strlen_zero(tmp)) {
7089 ast_module_user_remove(u);
7090 return 0;
7092 args.argv0 = ast_strdupa(tmp);
7095 res = leave_voicemail(chan, args.argv0, &leave_options);
7097 if (res == ERROR_LOCK_PATH) {
7098 ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
7099 /*Send the call to n+101 priority, where n is the current priority*/
7100 if (ast_test_flag(&leave_options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
7101 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
7102 ast_log(LOG_WARNING, "Extension %s, priority %d doesn't exist.\n", chan->exten, chan->priority + 101);
7103 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7104 res = 0;
7107 ast_module_user_remove(u);
7109 return res;
7112 static struct ast_vm_user *find_or_create(char *context, char *mbox)
7114 struct ast_vm_user *vmu;
7115 AST_LIST_TRAVERSE(&users, vmu, list) {
7116 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mbox, vmu->mailbox))
7117 break;
7118 if (context && (!strcasecmp(context, vmu->context)) && (!strcasecmp(mbox, vmu->mailbox)))
7119 break;
7122 if (!vmu) {
7123 if ((vmu = ast_calloc(1, sizeof(*vmu)))) {
7124 ast_copy_string(vmu->context, context, sizeof(vmu->context));
7125 ast_copy_string(vmu->mailbox, mbox, sizeof(vmu->mailbox));
7126 AST_LIST_INSERT_TAIL(&users, vmu, list);
7129 return vmu;
7132 static int append_mailbox(char *context, char *mbox, char *data)
7134 /* Assumes lock is already held */
7135 char *tmp;
7136 char *stringp;
7137 char *s;
7138 struct ast_vm_user *vmu;
7140 tmp = ast_strdupa(data);
7142 if ((vmu = find_or_create(context, mbox))) {
7143 populate_defaults(vmu);
7145 stringp = tmp;
7146 if ((s = strsep(&stringp, ",")))
7147 ast_copy_string(vmu->password, s, sizeof(vmu->password));
7148 if (stringp && (s = strsep(&stringp, ",")))
7149 ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
7150 if (stringp && (s = strsep(&stringp, ",")))
7151 ast_copy_string(vmu->email, s, sizeof(vmu->email));
7152 if (stringp && (s = strsep(&stringp, ",")))
7153 ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
7154 if (stringp && (s = strsep(&stringp, ",")))
7155 apply_options(vmu, s);
7157 return 0;
7160 static int vm_box_exists(struct ast_channel *chan, void *data)
7162 struct ast_module_user *u;
7163 struct ast_vm_user svm;
7164 char *context, *box;
7165 int priority_jump = 0;
7166 AST_DECLARE_APP_ARGS(args,
7167 AST_APP_ARG(mbox);
7168 AST_APP_ARG(options);
7171 if (ast_strlen_zero(data)) {
7172 ast_log(LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
7173 return -1;
7176 u = ast_module_user_add(chan);
7178 box = ast_strdupa(data);
7180 AST_STANDARD_APP_ARGS(args, box);
7182 if (args.options) {
7183 if (strchr(args.options, 'j'))
7184 priority_jump = 1;
7187 if ((context = strchr(args.mbox, '@'))) {
7188 *context = '\0';
7189 context++;
7192 if (find_user(&svm, context, args.mbox)) {
7193 pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
7194 if (priority_jump || ast_opt_priority_jumping)
7195 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
7196 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);
7197 } else
7198 pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
7199 ast_module_user_remove(u);
7200 return 0;
7203 static int vmauthenticate(struct ast_channel *chan, void *data)
7205 struct ast_module_user *u;
7206 char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
7207 struct ast_vm_user vmus;
7208 char *options = NULL;
7209 int silent = 0, skipuser = 0;
7210 int res = -1;
7212 u = ast_module_user_add(chan);
7214 if (s) {
7215 s = ast_strdupa(s);
7216 user = strsep(&s, "|");
7217 options = strsep(&s, "|");
7218 if (user) {
7219 s = user;
7220 user = strsep(&s, "@");
7221 context = strsep(&s, "");
7222 if (!ast_strlen_zero(user))
7223 skipuser++;
7224 ast_copy_string(mailbox, user, sizeof(mailbox));
7228 if (options) {
7229 silent = (strchr(options, 's')) != NULL;
7232 if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
7233 pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
7234 pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
7235 ast_play_and_wait(chan, "auth-thankyou");
7236 res = 0;
7239 ast_module_user_remove(u);
7240 return res;
7243 static char voicemail_show_users_help[] =
7244 "Usage: voicemail show users [for <context>]\n"
7245 " Lists all mailboxes currently set up\n";
7247 static char voicemail_show_zones_help[] =
7248 "Usage: voicemail show zones\n"
7249 " Lists zone message formats\n";
7251 static int handle_voicemail_show_users(int fd, int argc, char *argv[])
7253 struct ast_vm_user *vmu;
7254 char *output_format = "%-10s %-5s %-25s %-10s %6s\n";
7256 if ((argc < 3) || (argc > 5) || (argc == 4)) return RESULT_SHOWUSAGE;
7257 else if ((argc == 5) && strcmp(argv[3],"for")) return RESULT_SHOWUSAGE;
7259 AST_LIST_LOCK(&users);
7260 if (!AST_LIST_EMPTY(&users)) {
7261 if (argc == 3)
7262 ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
7263 else {
7264 int count = 0;
7265 AST_LIST_TRAVERSE(&users, vmu, list) {
7266 if (!strcmp(argv[4],vmu->context))
7267 count++;
7269 if (count) {
7270 ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
7271 } else {
7272 ast_cli(fd, "No such voicemail context \"%s\"\n", argv[4]);
7273 AST_LIST_UNLOCK(&users);
7274 return RESULT_FAILURE;
7277 AST_LIST_TRAVERSE(&users, vmu, list) {
7278 int newmsgs = 0, oldmsgs = 0;
7279 char count[12], tmp[256] = "";
7281 if ((argc == 3) || ((argc == 5) && !strcmp(argv[4],vmu->context))) {
7282 snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
7283 inboxcount(tmp, &newmsgs, &oldmsgs);
7284 snprintf(count,sizeof(count),"%d",newmsgs);
7285 ast_cli(fd, output_format, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
7288 } else {
7289 ast_cli(fd, "There are no voicemail users currently defined\n");
7290 AST_LIST_UNLOCK(&users);
7291 return RESULT_FAILURE;
7293 AST_LIST_UNLOCK(&users);
7294 return RESULT_SUCCESS;
7297 static int handle_voicemail_show_zones(int fd, int argc, char *argv[])
7299 struct vm_zone *zone;
7300 char *output_format = "%-15s %-20s %-45s\n";
7301 int res = RESULT_SUCCESS;
7303 if (argc != 3)
7304 return RESULT_SHOWUSAGE;
7306 AST_LIST_LOCK(&zones);
7307 if (!AST_LIST_EMPTY(&zones)) {
7308 ast_cli(fd, output_format, "Zone", "Timezone", "Message Format");
7309 AST_LIST_TRAVERSE(&zones, zone, list) {
7310 ast_cli(fd, output_format, zone->name, zone->timezone, zone->msg_format);
7312 } else {
7313 ast_cli(fd, "There are no voicemail zones currently defined\n");
7314 res = RESULT_FAILURE;
7316 AST_LIST_UNLOCK(&zones);
7318 return res;
7321 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
7323 int which = 0;
7324 int wordlen;
7325 struct ast_vm_user *vmu;
7326 const char *context = "";
7328 /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
7329 if (pos > 4)
7330 return NULL;
7331 if (pos == 3)
7332 return (state == 0) ? ast_strdup("for") : NULL;
7333 wordlen = strlen(word);
7334 AST_LIST_TRAVERSE(&users, vmu, list) {
7335 if (!strncasecmp(word, vmu->context, wordlen)) {
7336 if (context && strcmp(context, vmu->context) && ++which > state)
7337 return ast_strdup(vmu->context);
7338 /* ignore repeated contexts ? */
7339 context = vmu->context;
7342 return NULL;
7345 static struct ast_cli_entry cli_show_voicemail_users_deprecated = {
7346 { "show", "voicemail", "users", NULL },
7347 handle_voicemail_show_users, NULL,
7348 NULL, complete_voicemail_show_users };
7350 static struct ast_cli_entry cli_show_voicemail_zones_deprecated = {
7351 { "show", "voicemail", "zones", NULL },
7352 handle_voicemail_show_zones, NULL,
7353 NULL, NULL };
7355 static struct ast_cli_entry cli_voicemail[] = {
7356 { { "voicemail", "show", "users", NULL },
7357 handle_voicemail_show_users, "List defined voicemail boxes",
7358 voicemail_show_users_help, complete_voicemail_show_users, &cli_show_voicemail_users_deprecated },
7360 { { "voicemail", "show", "zones", NULL },
7361 handle_voicemail_show_zones, "List zone message formats",
7362 voicemail_show_zones_help, NULL, &cli_show_voicemail_zones_deprecated },
7365 static int load_config(void)
7367 struct ast_vm_user *cur;
7368 struct vm_zone *zcur;
7369 struct ast_config *cfg, *ucfg;
7370 char *cat;
7371 struct ast_variable *var;
7372 const char *notifystr = NULL;
7373 const char *smdistr = NULL;
7374 const char *astattach;
7375 const char *astsearch;
7376 const char *astsaycid;
7377 const char *send_voicemail;
7378 #ifdef IMAP_STORAGE
7379 const char *imap_server;
7380 const char *imap_port;
7381 const char *imap_flags;
7382 const char *imap_folder;
7383 const char *auth_user;
7384 const char *auth_password;
7385 const char *expunge_on_hangup;
7386 #endif
7387 const char *astcallop;
7388 const char *astreview;
7389 const char *asttempgreetwarn;
7390 const char *astskipcmd;
7391 const char *asthearenv;
7392 const char *astsaydurationinfo;
7393 const char *astsaydurationminfo;
7394 const char *silencestr;
7395 const char *maxmsgstr;
7396 const char *astdirfwd;
7397 const char *thresholdstr;
7398 const char *fmt;
7399 const char *astemail;
7400 const char *ucontext;
7401 const char *astmailcmd = SENDMAIL;
7402 const char *astforcename;
7403 const char *astforcegreet;
7404 const char *s;
7405 char *q,*stringp;
7406 const char *dialoutcxt = NULL;
7407 const char *callbackcxt = NULL;
7408 const char *exitcxt = NULL;
7409 const char *extpc;
7410 const char *emaildateformatstr;
7411 const char *volgainstr;
7412 int x;
7413 int tmpadsi[4];
7415 cfg = ast_config_load(VOICEMAIL_CONFIG);
7417 AST_LIST_LOCK(&users);
7418 while ((cur = AST_LIST_REMOVE_HEAD(&users, list))) {
7419 ast_set_flag(cur, VM_ALLOCED);
7420 free_user(cur);
7423 AST_LIST_LOCK(&zones);
7424 while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
7425 free_zone(zcur);
7426 AST_LIST_UNLOCK(&zones);
7428 memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
7430 if (cfg) {
7431 /* General settings */
7433 if (!(ucontext = ast_variable_retrieve(cfg, "general", "userscontext")))
7434 ucontext = "default";
7435 ast_copy_string(userscontext, ucontext, sizeof(userscontext));
7436 /* Attach voice message to mail message ? */
7437 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach")))
7438 astattach = "yes";
7439 ast_set2_flag((&globalflags), ast_true(astattach), VM_ATTACH);
7441 if (!(astsearch = ast_variable_retrieve(cfg, "general", "searchcontexts")))
7442 astsearch = "no";
7443 ast_set2_flag((&globalflags), ast_true(astsearch), VM_SEARCH);
7445 volgain = 0.0;
7446 if ((volgainstr = ast_variable_retrieve(cfg, "general", "volgain")))
7447 sscanf(volgainstr, "%lf", &volgain);
7449 #ifdef ODBC_STORAGE
7450 strcpy(odbc_database, "asterisk");
7451 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
7452 ast_copy_string(odbc_database, thresholdstr, sizeof(odbc_database));
7454 strcpy(odbc_table, "voicemessages");
7455 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbctable"))) {
7456 ast_copy_string(odbc_table, thresholdstr, sizeof(odbc_table));
7458 #endif
7459 /* Mail command */
7460 strcpy(mailcmd, SENDMAIL);
7461 if ((astmailcmd = ast_variable_retrieve(cfg, "general", "mailcmd")))
7462 ast_copy_string(mailcmd, astmailcmd, sizeof(mailcmd)); /* User setting */
7464 maxsilence = 0;
7465 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
7466 maxsilence = atoi(silencestr);
7467 if (maxsilence > 0)
7468 maxsilence *= 1000;
7471 if (!(maxmsgstr = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
7472 maxmsg = MAXMSG;
7473 } else {
7474 maxmsg = atoi(maxmsgstr);
7475 if (maxmsg <= 0) {
7476 ast_log(LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", maxmsgstr, MAXMSG);
7477 maxmsg = MAXMSG;
7478 } else if (maxmsg > MAXMSGLIMIT) {
7479 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, maxmsgstr);
7480 maxmsg = MAXMSGLIMIT;
7484 /* Load date format config for voicemail mail */
7485 if ((emaildateformatstr = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
7486 ast_copy_string(emaildateformat, emaildateformatstr, sizeof(emaildateformat));
7489 /* External password changing command */
7490 if ((extpc = ast_variable_retrieve(cfg, "general", "externpass"))) {
7491 ast_copy_string(ext_pass_cmd,extpc,sizeof(ext_pass_cmd));
7493 #ifdef IMAP_STORAGE
7494 /* IMAP server address */
7495 if ((imap_server = ast_variable_retrieve(cfg, "general", "imapserver"))) {
7496 ast_copy_string(imapserver, imap_server, sizeof(imapserver));
7497 } else {
7498 ast_copy_string(imapserver,"localhost", sizeof(imapserver));
7500 /* IMAP server port */
7501 if ((imap_port = ast_variable_retrieve(cfg, "general", "imapport"))) {
7502 ast_copy_string(imapport, imap_port, sizeof(imapport));
7503 } else {
7504 ast_copy_string(imapport,"143", sizeof(imapport));
7506 /* IMAP server flags */
7507 if ((imap_flags = ast_variable_retrieve(cfg, "general", "imapflags"))) {
7508 ast_copy_string(imapflags, imap_flags, sizeof(imapflags));
7510 /* IMAP server master username */
7511 if ((auth_user = ast_variable_retrieve(cfg, "general", "authuser"))) {
7512 ast_copy_string(authuser, auth_user, sizeof(authuser));
7514 /* IMAP server master password */
7515 if ((auth_password = ast_variable_retrieve(cfg, "general", "authpassword"))) {
7516 ast_copy_string(authpassword, auth_password, sizeof(authpassword));
7518 /* Expunge on exit */
7519 if ((expunge_on_hangup = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
7520 if (ast_false(expunge_on_hangup))
7521 expungeonhangup = 0;
7522 else
7523 expungeonhangup = 1;
7524 } else {
7525 expungeonhangup = 1;
7527 /* IMAP voicemail folder */
7528 if ((imap_folder = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
7529 ast_copy_string(imapfolder, imap_folder, sizeof(imapfolder));
7530 } else {
7531 ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
7533 #endif
7534 /* External voicemail notify application */
7536 if ((notifystr = ast_variable_retrieve(cfg, "general", "externnotify"))) {
7537 ast_copy_string(externnotify, notifystr, sizeof(externnotify));
7538 if (option_debug > 2)
7539 ast_log(LOG_DEBUG, "found externnotify: %s\n", externnotify);
7540 if (!strcasecmp(externnotify, "smdi")) {
7541 if (option_debug)
7542 ast_log(LOG_DEBUG, "Using SMDI for external voicemail notification\n");
7543 if ((smdistr = ast_variable_retrieve(cfg, "general", "smdiport"))) {
7544 smdi_iface = ast_smdi_interface_find(smdistr);
7545 } else {
7546 if (option_debug)
7547 ast_log(LOG_DEBUG, "No SMDI interface set, trying default (/dev/ttyS0)\n");
7548 smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
7551 if (!smdi_iface) {
7552 ast_log(LOG_ERROR, "No valid SMDI interface specfied, disabling external voicemail notification\n");
7553 externnotify[0] = '\0';
7556 } else {
7557 externnotify[0] = '\0';
7560 /* Silence treshold */
7561 silencethreshold = 256;
7562 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
7563 silencethreshold = atoi(thresholdstr);
7565 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
7566 astemail = ASTERISK_USERNAME;
7567 ast_copy_string(serveremail, astemail, sizeof(serveremail));
7569 vmmaxmessage = 0;
7570 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
7571 if (sscanf(s, "%d", &x) == 1) {
7572 vmmaxmessage = x;
7573 } else {
7574 ast_log(LOG_WARNING, "Invalid max message time length\n");
7578 vmminmessage = 0;
7579 if ((s = ast_variable_retrieve(cfg, "general", "minmessage"))) {
7580 if (sscanf(s, "%d", &x) == 1) {
7581 vmminmessage = x;
7582 if (maxsilence <= vmminmessage)
7583 ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
7584 } else {
7585 ast_log(LOG_WARNING, "Invalid min message time length\n");
7588 fmt = ast_variable_retrieve(cfg, "general", "format");
7589 if (!fmt)
7590 fmt = "wav";
7591 ast_copy_string(vmfmts, fmt, sizeof(vmfmts));
7593 skipms = 3000;
7594 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
7595 if (sscanf(s, "%d", &x) == 1) {
7596 maxgreet = x;
7597 } else {
7598 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
7602 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
7603 if (sscanf(s, "%d", &x) == 1) {
7604 skipms = x;
7605 } else {
7606 ast_log(LOG_WARNING, "Invalid skipms value\n");
7610 maxlogins = 3;
7611 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
7612 if (sscanf(s, "%d", &x) == 1) {
7613 maxlogins = x;
7614 } else {
7615 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
7619 /* Force new user to record name ? */
7620 if (!(astforcename = ast_variable_retrieve(cfg, "general", "forcename")))
7621 astforcename = "no";
7622 ast_set2_flag((&globalflags), ast_true(astforcename), VM_FORCENAME);
7624 /* Force new user to record greetings ? */
7625 if (!(astforcegreet = ast_variable_retrieve(cfg, "general", "forcegreetings")))
7626 astforcegreet = "no";
7627 ast_set2_flag((&globalflags), ast_true(astforcegreet), VM_FORCEGREET);
7629 if ((s = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))){
7630 if (option_debug > 2)
7631 ast_log(LOG_DEBUG,"VM_CID Internal context string: %s\n",s);
7632 stringp = ast_strdupa(s);
7633 for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
7634 if (!ast_strlen_zero(stringp)) {
7635 q = strsep(&stringp,",");
7636 while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
7637 q++;
7638 ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
7639 if (option_debug > 2)
7640 ast_log(LOG_DEBUG,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
7641 } else {
7642 cidinternalcontexts[x][0] = '\0';
7646 if (!(astreview = ast_variable_retrieve(cfg, "general", "review"))){
7647 if (option_debug)
7648 ast_log(LOG_DEBUG,"VM Review Option disabled globally\n");
7649 astreview = "no";
7651 ast_set2_flag((&globalflags), ast_true(astreview), VM_REVIEW);
7653 /*Temperary greeting reminder */
7654 if (!(asttempgreetwarn = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
7655 if (option_debug)
7656 ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option disabled globally\n");
7657 asttempgreetwarn = "no";
7658 } else {
7659 if (option_debug)
7660 ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option enabled globally\n");
7662 ast_set2_flag((&globalflags), ast_true(asttempgreetwarn), VM_TEMPGREETWARN);
7664 if (!(astcallop = ast_variable_retrieve(cfg, "general", "operator"))){
7665 if (option_debug)
7666 ast_log(LOG_DEBUG,"VM Operator break disabled globally\n");
7667 astcallop = "no";
7669 ast_set2_flag((&globalflags), ast_true(astcallop), VM_OPERATOR);
7671 if (!(astsaycid = ast_variable_retrieve(cfg, "general", "saycid"))) {
7672 if (option_debug)
7673 ast_log(LOG_DEBUG,"VM CID Info before msg disabled globally\n");
7674 astsaycid = "no";
7676 ast_set2_flag((&globalflags), ast_true(astsaycid), VM_SAYCID);
7678 if (!(send_voicemail = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
7679 if (option_debug)
7680 ast_log(LOG_DEBUG,"Send Voicemail msg disabled globally\n");
7681 send_voicemail = "no";
7683 ast_set2_flag((&globalflags), ast_true(send_voicemail), VM_SVMAIL);
7685 if (!(asthearenv = ast_variable_retrieve(cfg, "general", "envelope"))) {
7686 if (option_debug)
7687 ast_log(LOG_DEBUG,"ENVELOPE before msg enabled globally\n");
7688 asthearenv = "yes";
7690 ast_set2_flag((&globalflags), ast_true(asthearenv), VM_ENVELOPE);
7692 if (!(astsaydurationinfo = ast_variable_retrieve(cfg, "general", "sayduration"))) {
7693 if (option_debug)
7694 ast_log(LOG_DEBUG,"Duration info before msg enabled globally\n");
7695 astsaydurationinfo = "yes";
7697 ast_set2_flag((&globalflags), ast_true(astsaydurationinfo), VM_SAYDURATION);
7699 saydurationminfo = 2;
7700 if ((astsaydurationminfo = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
7701 if (sscanf(astsaydurationminfo, "%d", &x) == 1) {
7702 saydurationminfo = x;
7703 } else {
7704 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
7708 if (!(astskipcmd = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
7709 if (option_debug)
7710 ast_log(LOG_DEBUG,"We are not going to skip to the next msg after save/delete\n");
7711 astskipcmd = "no";
7713 ast_set2_flag((&globalflags), ast_true(astskipcmd), VM_SKIPAFTERCMD);
7715 if ((dialoutcxt = ast_variable_retrieve(cfg, "general", "dialout"))) {
7716 ast_copy_string(dialcontext, dialoutcxt, sizeof(dialcontext));
7717 if (option_debug)
7718 ast_log(LOG_DEBUG, "found dialout context: %s\n", dialcontext);
7719 } else {
7720 dialcontext[0] = '\0';
7723 if ((callbackcxt = ast_variable_retrieve(cfg, "general", "callback"))) {
7724 ast_copy_string(callcontext, callbackcxt, sizeof(callcontext));
7725 if (option_debug)
7726 ast_log(LOG_DEBUG, "found callback context: %s\n", callcontext);
7727 } else {
7728 callcontext[0] = '\0';
7731 if ((exitcxt = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
7732 ast_copy_string(exitcontext, exitcxt, sizeof(exitcontext));
7733 if (option_debug)
7734 ast_log(LOG_DEBUG, "found operator context: %s\n", exitcontext);
7735 } else {
7736 exitcontext[0] = '\0';
7739 if (!(astdirfwd = ast_variable_retrieve(cfg, "general", "usedirectory")))
7740 astdirfwd = "no";
7741 ast_set2_flag((&globalflags), ast_true(astdirfwd), VM_DIRECFORWARD);
7742 if ((ucfg = ast_config_load("users.conf"))) {
7743 for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
7744 if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
7745 continue;
7746 if ((cur = find_or_create(userscontext, cat))) {
7747 populate_defaults(cur);
7748 apply_options_full(cur, ast_variable_browse(ucfg, cat));
7749 ast_copy_string(cur->context, userscontext, sizeof(cur->context));
7752 ast_config_destroy(ucfg);
7754 cat = ast_category_browse(cfg, NULL);
7755 while (cat) {
7756 if (strcasecmp(cat, "general")) {
7757 var = ast_variable_browse(cfg, cat);
7758 if (strcasecmp(cat, "zonemessages")) {
7759 /* Process mailboxes in this context */
7760 while (var) {
7761 append_mailbox(cat, var->name, var->value);
7762 var = var->next;
7764 } else {
7765 /* Timezones in this context */
7766 while (var) {
7767 struct vm_zone *z;
7768 if ((z = ast_malloc(sizeof(*z)))) {
7769 char *msg_format, *timezone;
7770 msg_format = ast_strdupa(var->value);
7771 timezone = strsep(&msg_format, "|");
7772 if (msg_format) {
7773 ast_copy_string(z->name, var->name, sizeof(z->name));
7774 ast_copy_string(z->timezone, timezone, sizeof(z->timezone));
7775 ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
7776 AST_LIST_LOCK(&zones);
7777 AST_LIST_INSERT_HEAD(&zones, z, list);
7778 AST_LIST_UNLOCK(&zones);
7779 } else {
7780 ast_log(LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
7781 free(z);
7783 } else {
7784 free(z);
7785 AST_LIST_UNLOCK(&users);
7786 ast_config_destroy(cfg);
7787 return -1;
7789 var = var->next;
7793 cat = ast_category_browse(cfg, cat);
7795 memset(fromstring,0,sizeof(fromstring));
7796 memset(pagerfromstring,0,sizeof(pagerfromstring));
7797 memset(emailtitle,0,sizeof(emailtitle));
7798 strcpy(charset, "ISO-8859-1");
7799 if (emailbody) {
7800 free(emailbody);
7801 emailbody = NULL;
7803 if (emailsubject) {
7804 free(emailsubject);
7805 emailsubject = NULL;
7807 if (pagerbody) {
7808 free(pagerbody);
7809 pagerbody = NULL;
7811 if (pagersubject) {
7812 free(pagersubject);
7813 pagersubject = NULL;
7815 if ((s = ast_variable_retrieve(cfg, "general", "pbxskip")))
7816 ast_set2_flag((&globalflags), ast_true(s), VM_PBXSKIP);
7817 if ((s = ast_variable_retrieve(cfg, "general", "fromstring")))
7818 ast_copy_string(fromstring,s,sizeof(fromstring));
7819 if ((s = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
7820 ast_copy_string(pagerfromstring,s,sizeof(pagerfromstring));
7821 if ((s = ast_variable_retrieve(cfg, "general", "charset")))
7822 ast_copy_string(charset,s,sizeof(charset));
7823 if ((s = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
7824 sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
7825 for (x = 0; x < 4; x++) {
7826 memcpy(&adsifdn[x], &tmpadsi[x], 1);
7829 if ((s = ast_variable_retrieve(cfg, "general", "adsisec"))) {
7830 sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
7831 for (x = 0; x < 4; x++) {
7832 memcpy(&adsisec[x], &tmpadsi[x], 1);
7835 if ((s = ast_variable_retrieve(cfg, "general", "adsiver")))
7836 if (atoi(s)) {
7837 adsiver = atoi(s);
7839 if ((s = ast_variable_retrieve(cfg, "general", "emailtitle"))) {
7840 ast_log(LOG_NOTICE, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
7841 ast_copy_string(emailtitle,s,sizeof(emailtitle));
7843 if ((s = ast_variable_retrieve(cfg, "general", "emailsubject")))
7844 emailsubject = ast_strdup(s);
7845 if ((s = ast_variable_retrieve(cfg, "general", "emailbody"))) {
7846 char *tmpread, *tmpwrite;
7847 emailbody = ast_strdup(s);
7849 /* substitute strings \t and \n into the appropriate characters */
7850 tmpread = tmpwrite = emailbody;
7851 while ((tmpwrite = strchr(tmpread,'\\'))) {
7852 switch (tmpwrite[1]) {
7853 case 'r':
7854 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7855 *tmpwrite = '\r';
7856 break;
7857 case 'n':
7858 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7859 *tmpwrite = '\n';
7860 break;
7861 case 't':
7862 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7863 *tmpwrite = '\t';
7864 break;
7865 default:
7866 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
7868 tmpread = tmpwrite + 1;
7871 if ((s = ast_variable_retrieve(cfg, "general", "pagersubject")))
7872 pagersubject = ast_strdup(s);
7873 if ((s = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
7874 char *tmpread, *tmpwrite;
7875 pagerbody = ast_strdup(s);
7877 /* substitute strings \t and \n into the appropriate characters */
7878 tmpread = tmpwrite = pagerbody;
7879 while ((tmpwrite = strchr(tmpread, '\\'))) {
7880 switch (tmpwrite[1]) {
7881 case 'r':
7882 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7883 *tmpwrite = '\r';
7884 break;
7885 case 'n':
7886 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7887 *tmpwrite = '\n';
7888 break;
7889 case 't':
7890 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7891 *tmpwrite = '\t';
7892 break;
7893 default:
7894 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
7896 tmpread = tmpwrite + 1;
7899 AST_LIST_UNLOCK(&users);
7900 ast_config_destroy(cfg);
7901 return 0;
7902 } else {
7903 AST_LIST_UNLOCK(&users);
7904 ast_log(LOG_WARNING, "Failed to load configuration file.\n");
7905 return 0;
7909 static int reload(void)
7911 return(load_config());
7914 static int unload_module(void)
7916 int res;
7918 res = ast_unregister_application(app);
7919 res |= ast_unregister_application(app2);
7920 res |= ast_unregister_application(app3);
7921 res |= ast_unregister_application(app4);
7922 ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
7923 ast_uninstall_vm_functions();
7925 ast_module_user_hangup_all();
7927 return res;
7930 static int load_module(void)
7932 int res;
7933 char *adsi_loaded = ast_module_helper("", "res_adsi.so", 0, 0, 0, 0);
7934 free(adsi_loaded);
7935 if (!adsi_loaded) {
7936 /* If embedded, res_adsi may be known as "res_adsi" not "res_adsi.so" */
7937 adsi_loaded = ast_module_helper("", "res_adsi", 0, 0, 0, 0);
7938 ast_free(adsi_loaded);
7939 if (!adsi_loaded) {
7940 ast_log(LOG_ERROR, "app_voicemail.so depends upon res_adsi.so\n");
7941 return AST_MODULE_LOAD_DECLINE;
7945 my_umask = umask(0);
7946 umask(my_umask);
7947 res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
7948 res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
7949 res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
7950 res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
7951 if (res)
7952 return(res);
7954 if ((res=load_config())) {
7955 return(res);
7958 ast_cli_register_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
7960 /* compute the location of the voicemail spool directory */
7961 snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
7963 ast_install_vm_functions(has_voicemail, inboxcount, messagecount);
7965 return res;
7968 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
7970 int cmd = 0;
7971 char destination[80] = "";
7972 int retries = 0;
7974 if (!num) {
7975 if (option_verbose > 2)
7976 ast_verbose( VERBOSE_PREFIX_3 "Destination number will be entered manually\n");
7977 while (retries < 3 && cmd != 't') {
7978 destination[1] = '\0';
7979 destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
7980 if (!cmd)
7981 destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
7982 if (!cmd)
7983 destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
7984 if (!cmd) {
7985 cmd = ast_waitfordigit(chan, 6000);
7986 if (cmd)
7987 destination[0] = cmd;
7989 if (!cmd) {
7990 retries++;
7991 } else {
7993 if (cmd < 0)
7994 return 0;
7995 if (cmd == '*') {
7996 if (option_verbose > 2)
7997 ast_verbose( VERBOSE_PREFIX_3 "User hit '*' to cancel outgoing call\n");
7998 return 0;
8000 if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0)
8001 retries++;
8002 else
8003 cmd = 't';
8006 if (retries >= 3) {
8007 return 0;
8010 } else {
8011 if (option_verbose > 2)
8012 ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
8013 ast_copy_string(destination, num, sizeof(destination));
8016 if (!ast_strlen_zero(destination)) {
8017 if (destination[strlen(destination) -1 ] == '*')
8018 return 0;
8019 if (option_verbose > 2)
8020 ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
8021 ast_copy_string(chan->exten, destination, sizeof(chan->exten));
8022 ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
8023 chan->priority = 0;
8024 return 9;
8026 return 0;
8029 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)
8031 int res = 0;
8032 #ifdef IMAP_STORAGE
8033 char origtimeS[256],cidS[256],contextS[256];
8034 char *header_content,*temp;
8035 #endif
8036 char filename[PATH_MAX];
8037 struct ast_config *msg_cfg = NULL;
8038 const char *origtime, *context;
8039 char *cid, *name, *num;
8040 int retries = 0;
8042 vms->starting = 0;
8043 #ifdef IMAP_STORAGE
8044 /* START HERE */
8045 /* get the message info!! */
8046 if (option_debug > 2)
8047 ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
8048 if (vms->msgArray[vms->curmsg] == 0) {
8049 ast_log (LOG_WARNING,"Trying to access unknown message\n");
8050 return -1;
8053 /* This will only work for new messages... */
8054 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
8055 /* empty string means no valid header */
8056 if (ast_strlen_zero(header_content)) {
8057 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
8058 return -1;
8061 /* Get info from headers!! */
8062 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:");
8064 if (temp)
8065 ast_copy_string(cidS,temp, sizeof(cidS));
8066 else
8067 cidS[0] = '\0';
8069 cid = &cidS[0];
8070 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Context:");
8072 if (temp)
8073 ast_copy_string(contextS,temp, sizeof(contextS));
8074 else
8075 contextS[0] = '\0';
8077 context = &contextS[0];
8078 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:");
8080 if (temp)
8081 ast_copy_string(origtimeS,temp, sizeof(origtimeS));
8082 else
8083 origtimeS[0] = '\0';
8085 origtime = &origtimeS[0];
8087 ast_copy_string(filename, "IMAP_STORAGE", sizeof(filename));
8088 #else
8089 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
8091 /* Retrieve info from VM attribute file */
8093 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
8094 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
8095 RETRIEVE(vms->curdir, vms->curmsg);
8096 msg_cfg = ast_config_load(filename);
8097 DISPOSE(vms->curdir, vms->curmsg);
8098 if (!msg_cfg) {
8099 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
8100 return 0;
8103 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
8104 ast_config_destroy(msg_cfg);
8105 return 0;
8108 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
8110 context = ast_variable_retrieve(msg_cfg, "message", "context");
8111 if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
8112 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
8113 #endif
8114 switch (option) {
8115 case 3:
8116 if (!res)
8117 res = play_message_datetime(chan, vmu, origtime, filename);
8118 if (!res)
8119 res = play_message_callerid(chan, vms, cid, context, 0);
8121 res = 't';
8122 break;
8124 case 2: /* Call back */
8126 if (ast_strlen_zero(cid))
8127 break;
8129 ast_callerid_parse(cid, &name, &num);
8130 while ((res > -1) && (res != 't')) {
8131 switch (res) {
8132 case '1':
8133 if (num) {
8134 /* Dial the CID number */
8135 res = dialout(chan, vmu, num, vmu->callback);
8136 if (res) {
8137 ast_config_destroy(msg_cfg);
8138 return 9;
8140 } else {
8141 res = '2';
8143 break;
8145 case '2':
8146 /* Want to enter a different number, can only do this if there's a dialout context for this user */
8147 if (!ast_strlen_zero(vmu->dialout)) {
8148 res = dialout(chan, vmu, NULL, vmu->dialout);
8149 if (res) {
8150 ast_config_destroy(msg_cfg);
8151 return 9;
8153 } else {
8154 if (option_verbose > 2)
8155 ast_verbose( VERBOSE_PREFIX_3 "Caller can not specify callback number - no dialout context available\n");
8156 res = ast_play_and_wait(chan, "vm-sorry");
8158 ast_config_destroy(msg_cfg);
8159 return res;
8160 case '*':
8161 res = 't';
8162 break;
8163 case '3':
8164 case '4':
8165 case '5':
8166 case '6':
8167 case '7':
8168 case '8':
8169 case '9':
8170 case '0':
8172 res = ast_play_and_wait(chan, "vm-sorry");
8173 retries++;
8174 break;
8175 default:
8176 if (num) {
8177 if (option_verbose > 2)
8178 ast_verbose( VERBOSE_PREFIX_3 "Confirm CID number '%s' is number to use for callback\n", num);
8179 res = ast_play_and_wait(chan, "vm-num-i-have");
8180 if (!res)
8181 res = play_message_callerid(chan, vms, num, vmu->context, 1);
8182 if (!res)
8183 res = ast_play_and_wait(chan, "vm-tocallnum");
8184 /* Only prompt for a caller-specified number if there is a dialout context specified */
8185 if (!ast_strlen_zero(vmu->dialout)) {
8186 if (!res)
8187 res = ast_play_and_wait(chan, "vm-calldiffnum");
8189 } else {
8190 res = ast_play_and_wait(chan, "vm-nonumber");
8191 if (!ast_strlen_zero(vmu->dialout)) {
8192 if (!res)
8193 res = ast_play_and_wait(chan, "vm-toenternumber");
8196 if (!res)
8197 res = ast_play_and_wait(chan, "vm-star-cancel");
8198 if (!res)
8199 res = ast_waitfordigit(chan, 6000);
8200 if (!res) {
8201 retries++;
8202 if (retries > 3)
8203 res = 't';
8205 break;
8208 if (res == 't')
8209 res = 0;
8210 else if (res == '*')
8211 res = -1;
8213 break;
8215 case 1: /* Reply */
8216 /* Send reply directly to sender */
8217 if (ast_strlen_zero(cid))
8218 break;
8220 ast_callerid_parse(cid, &name, &num);
8221 if (!num) {
8222 if (option_verbose > 2)
8223 ast_verbose(VERBOSE_PREFIX_3 "No CID number available, no reply sent\n");
8224 if (!res)
8225 res = ast_play_and_wait(chan, "vm-nonumber");
8226 ast_config_destroy(msg_cfg);
8227 return res;
8228 } else {
8229 struct ast_vm_user vmu2;
8230 if (find_user(&vmu2, vmu->context, num)) {
8231 struct leave_vm_options leave_options;
8232 char mailbox[AST_MAX_EXTENSION * 2 + 2];
8233 snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
8235 if (option_verbose > 2)
8236 ast_verbose(VERBOSE_PREFIX_3 "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
8238 memset(&leave_options, 0, sizeof(leave_options));
8239 leave_options.record_gain = record_gain;
8240 res = leave_voicemail(chan, mailbox, &leave_options);
8241 if (!res)
8242 res = 't';
8243 ast_config_destroy(msg_cfg);
8244 return res;
8245 } else {
8246 /* Sender has no mailbox, can't reply */
8247 if (option_verbose > 2)
8248 ast_verbose( VERBOSE_PREFIX_3 "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
8249 ast_play_and_wait(chan, "vm-nobox");
8250 res = 't';
8251 ast_config_destroy(msg_cfg);
8252 return res;
8255 res = 0;
8257 break;
8260 #ifndef IMAP_STORAGE
8261 ast_config_destroy(msg_cfg);
8263 if (!res) {
8264 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
8265 vms->heard[msg] = 1;
8266 res = wait_file(chan, vms, vms->fn);
8268 #endif
8269 return res;
8272 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
8273 int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
8274 signed char record_gain, struct vm_state *vms)
8276 /* Record message & let caller review or re-record it, or set options if applicable */
8277 int res = 0;
8278 int cmd = 0;
8279 int max_attempts = 3;
8280 int attempts = 0;
8281 int recorded = 0;
8282 int message_exists = 0;
8283 signed char zero_gain = 0;
8284 char tempfile[PATH_MAX];
8285 char *acceptdtmf = "#";
8286 char *canceldtmf = "";
8288 /* Note that urgent and private are for flagging messages as such in the future */
8290 /* barf if no pointer passed to store duration in */
8291 if (duration == NULL) {
8292 ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
8293 return -1;
8296 if (!outsidecaller)
8297 snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
8298 else
8299 ast_copy_string(tempfile, recordfile, sizeof(tempfile));
8301 cmd = '3'; /* Want to start by recording */
8303 while ((cmd >= 0) && (cmd != 't')) {
8304 switch (cmd) {
8305 case '1':
8306 if (!message_exists) {
8307 /* In this case, 1 is to record a message */
8308 cmd = '3';
8309 break;
8310 } else {
8311 /* Otherwise 1 is to save the existing message */
8312 if (option_verbose > 2)
8313 ast_verbose(VERBOSE_PREFIX_3 "Saving message as is\n");
8314 if (!outsidecaller)
8315 ast_filerename(tempfile, recordfile, NULL);
8316 ast_stream_and_wait(chan, "vm-msgsaved", chan->language, "");
8317 if (!outsidecaller) {
8318 STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms);
8319 DISPOSE(recordfile, -1);
8321 cmd = 't';
8322 return res;
8324 case '2':
8325 /* Review */
8326 if (option_verbose > 2)
8327 ast_verbose(VERBOSE_PREFIX_3 "Reviewing the message\n");
8328 cmd = ast_stream_and_wait(chan, tempfile, chan->language, AST_DIGIT_ANY);
8329 break;
8330 case '3':
8331 message_exists = 0;
8332 /* Record */
8333 if (recorded == 1) {
8334 if (option_verbose > 2)
8335 ast_verbose(VERBOSE_PREFIX_3 "Re-recording the message\n");
8336 } else {
8337 if (option_verbose > 2)
8338 ast_verbose(VERBOSE_PREFIX_3 "Recording the message\n");
8340 if (recorded && outsidecaller) {
8341 cmd = ast_play_and_wait(chan, INTRO);
8342 cmd = ast_play_and_wait(chan, "beep");
8344 recorded = 1;
8345 /* 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 */
8346 if (record_gain)
8347 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
8348 if (ast_test_flag(vmu, VM_OPERATOR))
8349 canceldtmf = "0";
8350 cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
8351 if (record_gain)
8352 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
8353 if (cmd == -1) {
8354 /* User has hung up, no options to give */
8355 if (!outsidecaller) {
8356 /* user was recording a greeting and they hung up, so let's delete the recording. */
8357 ast_filedelete(tempfile, NULL);
8359 return cmd;
8361 if (cmd == '0') {
8362 break;
8363 } else if (cmd == '*') {
8364 break;
8366 #if 0
8367 else if (vmu->review && (*duration < 5)) {
8368 /* Message is too short */
8369 if (option_verbose > 2)
8370 ast_verbose(VERBOSE_PREFIX_3 "Message too short\n");
8371 cmd = ast_play_and_wait(chan, "vm-tooshort");
8372 cmd = ast_filedelete(tempfile, NULL);
8373 break;
8375 else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
8376 /* Message is all silence */
8377 if (option_verbose > 2)
8378 ast_verbose(VERBOSE_PREFIX_3 "Nothing recorded\n");
8379 cmd = ast_filedelete(tempfile, NULL);
8380 cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
8381 if (!cmd)
8382 cmd = ast_play_and_wait(chan, "vm-speakup");
8383 break;
8385 #endif
8386 else {
8387 /* If all is well, a message exists */
8388 message_exists = 1;
8389 cmd = 0;
8391 break;
8392 case '4':
8393 case '5':
8394 case '6':
8395 case '7':
8396 case '8':
8397 case '9':
8398 case '*':
8399 case '#':
8400 cmd = ast_play_and_wait(chan, "vm-sorry");
8401 break;
8402 #if 0
8403 /* XXX Commented out for the moment because of the dangers of deleting
8404 a message while recording (can put the message numbers out of sync) */
8405 case '*':
8406 /* Cancel recording, delete message, offer to take another message*/
8407 cmd = ast_play_and_wait(chan, "vm-deleted");
8408 cmd = ast_filedelete(tempfile, NULL);
8409 if (outsidecaller) {
8410 res = vm_exec(chan, NULL);
8411 return res;
8413 else
8414 return 1;
8415 #endif
8416 case '0':
8417 if (!ast_test_flag(vmu, VM_OPERATOR)) {
8418 cmd = ast_play_and_wait(chan, "vm-sorry");
8419 break;
8421 if (message_exists || recorded) {
8422 cmd = ast_play_and_wait(chan, "vm-saveoper");
8423 if (!cmd)
8424 cmd = ast_waitfordigit(chan, 3000);
8425 if (cmd == '1') {
8426 ast_play_and_wait(chan, "vm-msgsaved");
8427 cmd = '0';
8428 } else {
8429 ast_play_and_wait(chan, "vm-deleted");
8430 DELETE(recordfile, -1, recordfile);
8431 cmd = '0';
8434 return cmd;
8435 default:
8436 /* If the caller is an ouside caller, and the review option is enabled,
8437 allow them to review the message, but let the owner of the box review
8438 their OGM's */
8439 if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
8440 return cmd;
8441 if (message_exists) {
8442 cmd = ast_play_and_wait(chan, "vm-review");
8444 else {
8445 cmd = ast_play_and_wait(chan, "vm-torerecord");
8446 if (!cmd)
8447 cmd = ast_waitfordigit(chan, 600);
8450 if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
8451 cmd = ast_play_and_wait(chan, "vm-reachoper");
8452 if (!cmd)
8453 cmd = ast_waitfordigit(chan, 600);
8455 #if 0
8456 if (!cmd)
8457 cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
8458 #endif
8459 if (!cmd)
8460 cmd = ast_waitfordigit(chan, 6000);
8461 if (!cmd) {
8462 attempts++;
8464 if (attempts > max_attempts) {
8465 cmd = 't';
8469 if (outsidecaller)
8470 ast_play_and_wait(chan, "vm-goodbye");
8471 if (cmd == 't')
8472 cmd = 0;
8473 return cmd;
8476 #ifdef IMAP_STORAGE
8478 static void write_file(char *filename, char *buffer, unsigned long len)
8480 FILE *output;
8482 output = fopen (filename, "w");
8483 fwrite (buffer, len, 1, output);
8484 fclose (output);
8487 void mm_searched(MAILSTREAM *stream, unsigned long number)
8489 struct vm_state *vms;
8490 char *mailbox;
8491 char *user;
8492 mailbox = stream->mailbox;
8493 user = get_user_by_mailbox(mailbox);
8494 vms = get_vm_state_by_imapuser(user,2);
8495 if (vms) {
8496 if (option_debug > 2)
8497 ast_log (LOG_DEBUG, "saving mailbox message number %lu as message %d. Interactive set to %d\n",number,vms->vmArrayIndex,vms->interactive);
8498 vms->msgArray[vms->vmArrayIndex++] = number;
8499 } else {
8500 ast_log (LOG_ERROR, "No state found.\n");
8505 #if 0 /*No need for this. */
8506 /* MM status report
8507 * Accepts: MAIL stream
8509 static void status(MAILSTREAM *stream)
8511 unsigned long i;
8512 char *s, date[MAILTMPLEN];
8513 THREADER *thr;
8514 AUTHENTICATOR *auth;
8515 rfc822_date (date);
8516 ast_log (LOG_NOTICE,"%s\n",date);
8517 if (stream) {
8518 if (stream->mailbox)
8519 ast_log (LOG_NOTICE," %s mailbox: %s, %lu messages, %lu recent\n",
8520 stream->dtb->name, stream->mailbox, stream->nmsgs,stream->recent);
8521 else
8522 ast_log (LOG_NOTICE,"No mailbox is open on this stream\n");
8523 if (stream->user_flags[0]) {
8524 ast_log (LOG_NOTICE,"Keywords: %s\n", stream->user_flags[0]);
8525 for (i = 1; i < NUSERFLAGS && stream->user_flags[i]; ++i)
8526 ast_log (LOG_NOTICE," %s\n", stream->user_flags[i]);
8528 if (!strcmp (stream->dtb->name, "imap")) {
8529 if (LEVELIMAP4rev1 (stream))
8530 s = "IMAP4rev1 (RFC 3501)";
8531 else if (LEVEL1730 (stream))
8532 s = "IMAP4 (RFC 1730)";
8533 else if (LEVELIMAP2bis (stream))
8534 s = "IMAP2bis";
8535 else if (LEVEL1176 (stream))
8536 s = "IMAP2 (RFC 1176)";
8537 else
8538 s = "IMAP2 (RFC 1064)";
8539 ast_log (LOG_NOTICE,"%s server %s\n", s, imap_host (stream));
8540 if (LEVELIMAP4 (stream)) {
8541 if ((i = (imap_cap(stream)->auth))) {
8542 s = "";
8543 ast_log (LOG_NOTICE,"Mutually-supported SASL mechanisms:\n");
8544 while ((auth = mail_lookup_auth (find_rightmost_bit (&i) + 1))) {
8545 ast_log (LOG_NOTICE," %s\n", auth->name);
8546 if (!strcmp (auth->name, "PLAIN"))
8547 s = "\n [LOGIN will not be listed here if PLAIN is supported]\n";
8549 ast_log (LOG_NOTICE,s);
8551 ast_log (LOG_NOTICE,"Supported standard extensions:\n");
8552 if (LEVELACL (stream))
8553 ast_log (LOG_NOTICE," Access Control lists (RFC 2086)\n");
8554 if (LEVELQUOTA (stream))
8555 ast_log (LOG_NOTICE," Quotas (RFC 2087)\n");
8556 if (LEVELLITERALPLUS (stream))
8557 ast_log (LOG_NOTICE," Non-synchronizing literals (RFC 2088)\n");
8558 if (LEVELIDLE (stream))
8559 ast_log (LOG_NOTICE," IDLE unsolicited update (RFC 2177)\n");
8560 if (LEVELMBX_REF (stream))
8561 ast_log (LOG_NOTICE," Mailbox referrals (RFC 2193)\n");
8562 if (LEVELLOG_REF (stream))
8563 ast_log (LOG_NOTICE," Login referrals (RFC 2221)\n");
8564 if (LEVELANONYMOUS (stream))
8565 ast_log (LOG_NOTICE," Anonymous access (RFC 2245)\n");
8566 if (LEVELNAMESPACE (stream))
8567 ast_log (LOG_NOTICE," Multiple namespaces (RFC 2342)\n");
8568 if (LEVELUIDPLUS (stream))
8569 ast_log (LOG_NOTICE," Extended UID behavior (RFC 2359)\n");
8570 if (LEVELSTARTTLS (stream))
8571 ast_log (LOG_NOTICE," Transport Layer Security (RFC 2595)\n");
8572 if (LEVELLOGINDISABLED (stream))
8573 ast_log (LOG_NOTICE," LOGIN command disabled (RFC 2595)\n");
8574 if (LEVELID (stream))
8575 ast_log (LOG_NOTICE," Implementation identity negotiation (RFC 2971)\n");
8576 if (LEVELCHILDREN (stream))
8577 ast_log (LOG_NOTICE," LIST children announcement (RFC 3348)\n");
8578 if (LEVELMULTIAPPEND (stream))
8579 ast_log (LOG_NOTICE," Atomic multiple APPEND (RFC 3502)\n");
8580 if (LEVELBINARY (stream))
8581 ast_log (LOG_NOTICE," Binary body content (RFC 3516)\n");
8582 ast_log (LOG_NOTICE,"Supported draft extensions:\n");
8583 if (LEVELUNSELECT (stream))
8584 ast_log (LOG_NOTICE," Mailbox unselect\n");
8585 if (LEVELSASLIR (stream))
8586 ast_log (LOG_NOTICE," SASL initial client response\n");
8587 if (LEVELSORT (stream))
8588 ast_log (LOG_NOTICE," Server-based sorting\n");
8589 if (LEVELTHREAD (stream)) {
8590 ast_log (LOG_NOTICE," Server-based threading:\n");
8591 for (thr = imap_cap(stream)->threader; thr; thr = thr->next)
8592 ast_log (LOG_NOTICE," %s\n", thr->name);
8594 if (LEVELSCAN (stream))
8595 ast_log (LOG_NOTICE," Mailbox text scan\n");
8596 if ((i = imap_cap(stream)->extlevel)) {
8597 ast_log (LOG_NOTICE,"Supported BODYSTRUCTURE extensions:\n");
8598 switch (i) {
8599 case BODYEXTLOC:
8600 ast_log (LOG_NOTICE," location\n");
8601 case BODYEXTLANG:
8602 ast_log (LOG_NOTICE," language\n");
8603 case BODYEXTDSP:
8604 ast_log (LOG_NOTICE," disposition\n");
8605 case BODYEXTMD5:
8606 ast_log (LOG_NOTICE," MD5\n");
8609 }else
8610 ast_log (LOG_NOTICE,"\n");
8614 #endif
8616 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
8618 struct ast_variable *var;
8619 struct ast_vm_user *vmu;
8621 vmu = ast_calloc(1, sizeof *vmu);
8622 if (!vmu)
8623 return NULL;
8624 ast_set_flag(vmu, VM_ALLOCED);
8625 populate_defaults(vmu);
8627 var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
8628 if (var) {
8629 apply_options_full(vmu, var);
8630 ast_variables_destroy(var);
8631 return vmu;
8632 } else {
8633 free(vmu);
8634 return NULL;
8638 /* Interfaces to C-client */
8640 void mm_exists(MAILSTREAM * stream, unsigned long number)
8642 /* mail_ping will callback here if new mail! */
8643 if (option_debug > 3)
8644 ast_log (LOG_DEBUG, "Entering EXISTS callback for message %ld\n", number);
8645 if (number == 0) return;
8646 set_update(stream);
8650 void mm_expunged(MAILSTREAM * stream, unsigned long number)
8652 /* mail_ping will callback here if expunged mail! */
8653 if (option_debug > 3)
8654 ast_log (LOG_DEBUG, "Entering EXPUNGE callback for message %ld\n", number);
8655 if (number == 0) return;
8656 set_update(stream);
8660 void mm_flags(MAILSTREAM * stream, unsigned long number)
8662 /* mail_ping will callback here if read mail! */
8663 if (option_debug > 3)
8664 ast_log (LOG_DEBUG, "Entering FLAGS callback for message %ld\n", number);
8665 if (number == 0) return;
8666 set_update(stream);
8670 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
8672 mm_log (string, errflg);
8676 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
8678 if (delimiter == '\0') {
8679 delimiter = delim;
8681 if (option_debug > 4) {
8682 ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
8683 if (attributes & LATT_NOINFERIORS)
8684 ast_log(LOG_DEBUG, "no inferiors\n");
8685 if (attributes & LATT_NOSELECT)
8686 ast_log(LOG_DEBUG, "no select\n");
8687 if (attributes & LATT_MARKED)
8688 ast_log(LOG_DEBUG, "marked\n");
8689 if (attributes & LATT_UNMARKED)
8690 ast_log(LOG_DEBUG, "unmarked\n");
8695 void mm_lsub(MAILSTREAM * stream, int delimiter, char *mailbox, long attributes)
8697 if (option_debug > 4) {
8698 ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delimiter, mailbox);
8699 if (attributes & LATT_NOINFERIORS)
8700 ast_log(LOG_DEBUG, "no inferiors\n");
8701 if (attributes & LATT_NOSELECT)
8702 ast_log(LOG_DEBUG, "no select\n");
8703 if (attributes & LATT_MARKED)
8704 ast_log(LOG_DEBUG, "marked\n");
8705 if (attributes & LATT_UNMARKED)
8706 ast_log(LOG_DEBUG, "unmarked\n");
8711 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
8713 ast_log (LOG_NOTICE," Mailbox %s", mailbox);
8714 if (status->flags & SA_MESSAGES)
8715 ast_log (LOG_NOTICE,", %lu messages", status->messages);
8716 if (status->flags & SA_RECENT)
8717 ast_log (LOG_NOTICE,", %lu recent", status->recent);
8718 if (status->flags & SA_UNSEEN)
8719 ast_log (LOG_NOTICE,", %lu unseen", status->unseen);
8720 if (status->flags & SA_UIDVALIDITY)
8721 ast_log (LOG_NOTICE,", %lu UID validity", status->uidvalidity);
8722 if (status->flags & SA_UIDNEXT)
8723 ast_log (LOG_NOTICE,", %lu next UID", status->uidnext);
8724 ast_log (LOG_NOTICE,"\n");
8728 void mm_log(char *string, long errflg)
8730 switch ((short) errflg) {
8731 case NIL:
8732 if (option_debug)
8733 ast_log(LOG_DEBUG,"IMAP Info: %s\n", string);
8734 break;
8735 case PARSE:
8736 case WARN:
8737 ast_log (LOG_WARNING,"IMAP Warning: %s\n", string);
8738 break;
8739 case ERROR:
8740 ast_log (LOG_ERROR,"IMAP Error: %s\n", string);
8741 break;
8746 void mm_dlog(char *string)
8748 ast_log (LOG_NOTICE, "%s\n", string);
8752 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
8754 struct ast_vm_user *vmu;
8756 if (option_debug > 3)
8757 ast_log(LOG_DEBUG, "Entering callback mm_login\n");
8759 ast_copy_string(user, mb->user, MAILTMPLEN);
8761 /* We should only do this when necessary */
8762 if (!ast_strlen_zero(authpassword)) {
8763 ast_copy_string(pwd, authpassword, MAILTMPLEN);
8764 } else {
8765 AST_LIST_TRAVERSE(&users, vmu, list) {
8766 if (!strcasecmp(mb->user, vmu->imapuser)) {
8767 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
8768 break;
8771 if (!vmu) {
8772 if ((vmu = find_user_realtime_imapuser(mb->user))) {
8773 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
8774 free_user(vmu);
8781 void mm_critical(MAILSTREAM * stream)
8786 void mm_nocritical(MAILSTREAM * stream)
8791 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
8793 kill (getpid (), SIGSTOP);
8794 return NIL;
8798 void mm_fatal(char *string)
8800 ast_log(LOG_ERROR,"IMAP access FATAL error: %s\n", string);
8803 /* C-client callback to handle quota */
8804 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
8806 struct vm_state *vms;
8807 char *mailbox;
8808 char *user;
8809 unsigned long usage = 0;
8810 unsigned long limit = 0;
8812 while (pquota) {
8813 usage = pquota->usage;
8814 limit = pquota->limit;
8815 pquota = pquota->next;
8818 mailbox = stream->mailbox;
8819 user = get_user_by_mailbox(mailbox);
8820 vms = get_vm_state_by_imapuser(user,2);
8821 if (vms) {
8822 if (option_debug > 2)
8823 ast_log (LOG_DEBUG, "User %s usage is %lu, limit is %lu\n",user,usage,limit);
8824 vms->quota_usage = usage;
8825 vms->quota_limit = limit;
8826 } else {
8827 ast_log (LOG_ERROR, "No state found.\n");
8831 static char *get_header_by_tag(char *header, char *tag)
8833 char *start;
8834 int taglen;
8835 char *eol_pnt;
8837 if (!header || !tag)
8838 return NULL;
8840 taglen = strlen(tag) + 1;
8841 if (taglen < 1)
8842 return NULL;
8844 start = strstr(header, tag);
8845 if (!start)
8846 return NULL;
8848 ast_mutex_lock(&imaptemp_lock);
8849 ast_copy_string(imaptemp, start+taglen, sizeof(imaptemp));
8850 ast_mutex_unlock(&imaptemp_lock);
8851 if ((eol_pnt = strchr(imaptemp,'\r')) || (eol_pnt = strchr(imaptemp,'\n')))
8852 *eol_pnt = '\0';
8853 return imaptemp;
8856 static char *get_user_by_mailbox(char *mailbox)
8858 char *start, *quote;
8859 char *eol_pnt;
8861 if (!mailbox)
8862 return NULL;
8864 start = strstr(mailbox,"/user=");
8865 if (!start)
8866 return NULL;
8868 ast_mutex_lock(&imaptemp_lock);
8869 ast_copy_string(imaptemp, start+6, sizeof(imaptemp));
8870 ast_mutex_unlock(&imaptemp_lock);
8872 quote = strchr(imaptemp,'\"');
8873 if (!quote) { /* if username is not in quotes */
8874 eol_pnt = strchr(imaptemp,'/');
8875 if (!eol_pnt) {
8876 eol_pnt = strchr(imaptemp,'}');
8878 *eol_pnt = '\0';
8879 return imaptemp;
8880 } else {
8881 eol_pnt = strchr(imaptemp+1,'\"');
8882 *eol_pnt = '\0';
8883 return imaptemp+1;
8887 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
8889 struct vmstate *vlist = NULL;
8891 ast_mutex_lock(&vmstate_lock);
8892 vlist = vmstates;
8893 while (vlist) {
8894 if (vlist->vms) {
8895 if (vlist->vms->imapuser) {
8896 if (!strcmp(vlist->vms->imapuser,user)) {
8897 if (interactive == 2) {
8898 ast_mutex_unlock(&vmstate_lock);
8899 return vlist->vms;
8900 } else if (vlist->vms->interactive == interactive) {
8901 ast_mutex_unlock(&vmstate_lock);
8902 return vlist->vms;
8905 } else {
8906 if (option_debug > 2)
8907 ast_log(LOG_DEBUG, " error: imapuser is NULL for %s\n",user);
8909 } else {
8910 if (option_debug > 2)
8911 ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",user);
8913 vlist = vlist->next;
8915 ast_mutex_unlock(&vmstate_lock);
8916 if (option_debug > 2)
8917 ast_log(LOG_DEBUG, "%s not found in vmstates\n",user);
8918 return NULL;
8921 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive)
8923 struct vmstate *vlist = NULL;
8925 ast_mutex_lock(&vmstate_lock);
8926 vlist = vmstates;
8927 if (option_debug > 2)
8928 ast_log(LOG_DEBUG, "Mailbox set to %s\n",mailbox);
8929 while (vlist) {
8930 if (vlist->vms) {
8931 if (vlist->vms->username) {
8932 if (option_debug > 2)
8933 ast_log(LOG_DEBUG, " comparing mailbox %s (i=%d) to vmstate mailbox %s (i=%d)\n",mailbox,interactive,vlist->vms->username,vlist->vms->interactive);
8934 if (!strcmp(vlist->vms->username,mailbox) && vlist->vms->interactive == interactive) {
8935 if (option_debug > 2)
8936 ast_log(LOG_DEBUG, " Found it!\n");
8937 ast_mutex_unlock(&vmstate_lock);
8938 return vlist->vms;
8940 } else {
8941 if (option_debug > 2)
8942 ast_log(LOG_DEBUG, " error: username is NULL for %s\n",mailbox);
8944 } else {
8945 if (option_debug > 2)
8946 ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",mailbox);
8948 vlist = vlist->next;
8950 ast_mutex_unlock(&vmstate_lock);
8951 if (option_debug > 2)
8952 ast_log(LOG_DEBUG, "%s not found in vmstates\n",mailbox);
8953 return NULL;
8956 static void vmstate_insert(struct vm_state *vms)
8958 struct vmstate *v;
8959 struct vm_state *altvms;
8961 /* If interactive, it probably already exists, and we should
8962 use the one we already have since it is more up to date.
8963 We can compare the username to find the duplicate */
8964 if (vms->interactive == 1) {
8965 altvms = get_vm_state_by_mailbox(vms->username,0);
8966 if (altvms) {
8967 if (option_debug > 2)
8968 ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
8969 vms->newmessages = altvms->newmessages;
8970 vms->oldmessages = altvms->oldmessages;
8971 if (option_debug > 2)
8972 ast_log(LOG_DEBUG, "check_msgArray before memcpy\n");
8973 check_msgArray(vms);
8974 /* memcpy(vms->msgArray, altvms->msgArray, sizeof(long)*256); */
8975 copy_msgArray(vms, altvms);
8976 if (option_debug > 2)
8977 ast_log(LOG_DEBUG, "check_msgArray after memcpy\n");
8978 check_msgArray(vms);
8979 vms->vmArrayIndex = altvms->vmArrayIndex;
8980 vms->lastmsg = altvms->lastmsg;
8981 vms->curmsg = altvms->curmsg;
8982 /* get a pointer to the persistent store */
8983 vms->persist_vms = altvms;
8984 /* Reuse the mailstream? */
8985 vms->mailstream = altvms->mailstream;
8986 /* vms->mailstream = NIL; */
8990 v = (struct vmstate *)malloc(sizeof(struct vmstate));
8991 if (!v) {
8992 ast_log(LOG_ERROR, "Out of memory\n");
8994 if (option_debug > 2)
8995 ast_log(LOG_DEBUG, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
8996 ast_mutex_lock(&vmstate_lock);
8997 v->vms = vms;
8998 v->next = vmstates;
8999 vmstates = v;
9000 ast_mutex_unlock(&vmstate_lock);
9003 static void vmstate_delete(struct vm_state *vms)
9005 struct vmstate *vc, *vf = NULL, *vl = NULL;
9006 struct vm_state *altvms;
9008 /* If interactive, we should copy pertainent info
9009 back to the persistent state (to make update immediate) */
9010 if (vms->interactive == 1) {
9011 altvms = vms->persist_vms;
9012 if (altvms) {
9013 if (option_debug > 2)
9014 ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
9015 altvms->newmessages = vms->newmessages;
9016 altvms->oldmessages = vms->oldmessages;
9017 altvms->updated = 1;
9021 ast_mutex_lock(&vmstate_lock);
9022 vc = vmstates;
9023 if (option_debug > 2)
9024 ast_log(LOG_DEBUG, "Removing vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
9025 while (vc) {
9026 if (vc->vms == vms) {
9027 vf = vc;
9028 if (vl)
9029 vl->next = vc->next;
9030 else
9031 vmstates = vc->next;
9032 break;
9034 vl = vc;
9035 vc = vc->next;
9037 if (!vf) {
9038 ast_log(LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n",vms->imapuser,vms->username);
9039 } else {
9040 ast_mutex_destroy(&vf->vms->lock);
9041 free(vf);
9043 ast_mutex_unlock(&vmstate_lock);
9046 static void set_update(MAILSTREAM * stream)
9048 struct vm_state *vms;
9049 char *mailbox;
9050 char *user;
9052 mailbox = stream->mailbox;
9053 user = get_user_by_mailbox(mailbox);
9054 vms = get_vm_state_by_imapuser(user, 0);
9055 if (vms) {
9056 if (option_debug > 2)
9057 ast_log (LOG_DEBUG, "User %s mailbox set for update.\n",user);
9058 vms->updated = 1; /* set updated flag since mailbox changed */
9059 } else {
9060 if (option_debug > 2)
9061 ast_log (LOG_WARNING, "User %s mailbox not found for update.\n",user);
9065 static void init_vm_state(struct vm_state *vms)
9067 int x;
9068 vms->vmArrayIndex = 0;
9069 for (x = 0; x < 256; x++) {
9070 vms->msgArray[x] = 0;
9072 ast_mutex_init(&vms->lock);
9075 static void check_msgArray(struct vm_state *vms)
9077 int x;
9078 for (x = 0; x<256; x++) {
9079 if (vms->msgArray[x]!=0) {
9080 if (option_debug)
9081 ast_log (LOG_DEBUG, "Item %d set to %ld\n",x,vms->msgArray[x]);
9086 static void copy_msgArray(struct vm_state *dst, struct vm_state *src)
9088 int x;
9089 for (x = 0; x<256; x++) {
9090 dst->msgArray[x] = src->msgArray[x];
9094 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format)
9096 char *body_content;
9097 char *body_decoded;
9098 unsigned long len;
9099 unsigned long newlen;
9100 char filename[256];
9102 if (!body || body == NIL)
9103 return -1;
9104 body_content = mail_fetchbody (vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
9105 if (body_content != NIL) {
9106 snprintf(filename, sizeof(filename), "%s.%s", vms->fn, format);
9107 /* ast_log (LOG_DEBUG,body_content); */
9108 body_decoded = rfc822_base64 ((unsigned char *)body_content, len, &newlen);
9109 write_file (filename, (char *) body_decoded, newlen);
9111 return 0;
9114 /* get delimiter via mm_list callback */
9115 static void get_mailbox_delimiter(MAILSTREAM *stream) {
9116 char tmp[50];
9117 snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
9118 mail_list(stream, tmp, "*");
9121 /* Check Quota for user */
9122 static void check_quota(struct vm_state *vms, char *mailbox) {
9123 mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
9124 if (option_debug > 2)
9125 ast_log(LOG_DEBUG, "Mailbox name set to: %s, about to check quotas\n", mailbox);
9126 if (vms && vms->mailstream != NULL) {
9127 imap_getquotaroot(vms->mailstream, mailbox);
9128 } else {
9129 ast_log(LOG_WARNING,"Mailstream not available for mailbox: %s\n",mailbox);
9133 #endif /* IMAP_STORAGE */
9135 /* This is a workaround so that menuselect displays a proper description
9136 * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
9139 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
9140 .load = load_module,
9141 .unload = unload_module,
9142 .reload = reload,