merge qwell's CLI verbification work
[asterisk-bristuff.git] / apps / app_voicemail.c
blobf0c9d985fde66e8739e206504ac22c4bd04b6b87
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 /*** MAKEOPTS
32 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o">
33 <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
34 <depend>unixodbc</depend>
35 <defaultenabled>no</defaultenabled>
36 </member>
37 <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
38 <depend>imap_tk</depend>
39 <use>ssl</use>
40 <defaultenabled>no</defaultenabled>
41 </member>
42 </category>
43 ***/
45 #include "asterisk.h"
47 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
49 #include <stdlib.h>
50 #include <errno.h>
51 #include <unistd.h>
52 #include <string.h>
53 #include <stdlib.h>
54 #include <stdio.h>
55 #include <sys/time.h>
56 #include <sys/stat.h>
57 #include <sys/types.h>
58 #include <sys/mman.h>
59 #include <time.h>
60 #include <dirent.h>
61 #ifdef IMAP_STORAGE
62 #include <ctype.h>
63 #include <signal.h>
64 #include <pwd.h>
65 #include "c-client.h"
66 #include "imap4r1.h"
67 #include "linkage.h"
68 #endif
69 #include "asterisk/lock.h"
70 #include "asterisk/file.h"
71 #include "asterisk/logger.h"
72 #include "asterisk/channel.h"
73 #include "asterisk/pbx.h"
74 #include "asterisk/options.h"
75 #include "asterisk/config.h"
76 #include "asterisk/say.h"
77 #include "asterisk/module.h"
78 #include "asterisk/adsi.h"
79 #include "asterisk/app.h"
80 #include "asterisk/manager.h"
81 #include "asterisk/dsp.h"
82 #include "asterisk/localtime.h"
83 #include "asterisk/cli.h"
84 #include "asterisk/utils.h"
85 #include "asterisk/stringfields.h"
86 #include "asterisk/smdi.h"
87 #ifdef ODBC_STORAGE
88 #include "asterisk/res_odbc.h"
89 #endif
91 #ifdef IMAP_STORAGE
92 AST_MUTEX_DEFINE_STATIC(curhstusr_lock);
93 static char *curhst = NIL; /* currently connected host */
94 static char *curusr = NIL; /* current login user */
96 static char temp[1024];
98 static char imapserver[48];
99 static char imapport[8];
100 static char imapflags[128];
101 static char authuser[32];
102 static char authpassword[42];
103 static int expungeonhangup = 1;
104 static char delimiter = '\0';
106 struct vm_state;
108 static int init_mailstream (struct vm_state *vms);
109 static void write_file (char *filename, char *buffer, unsigned long len);
110 static void status (MAILSTREAM *stream);
111 static void display_body (BODY *body, char *pfx, long i);
112 static char *get_header_by_tag(char *header, char *tag);
113 static void vm_imap_delete(int msgnum, struct vm_state *vms);
114 static char *get_user_by_mailbox(char *mailbox);
115 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
116 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
117 static void vmstate_insert(struct vm_state *vms);
118 static void vmstate_delete(struct vm_state *vms);
119 static void set_update(MAILSTREAM * stream);
120 static void init_vm_state(struct vm_state *vms);
121 static void check_msgArray(struct vm_state *vms);
122 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
123 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
124 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num);
125 static void get_mailbox_delimiter(MAILSTREAM *stream);
126 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
127 struct vmstate {
128 struct vm_state *vms;
129 struct vmstate *next;
131 AST_MUTEX_DEFINE_STATIC(vmstate_lock);
132 static struct vmstate *vmstates = NULL;
133 #endif
135 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
137 #define COMMAND_TIMEOUT 5000
138 /* Don't modify these here; set your umask at runtime instead */
139 #define VOICEMAIL_DIR_MODE 0777
140 #define VOICEMAIL_FILE_MODE 0666
142 #define VOICEMAIL_CONFIG "voicemail.conf"
143 #define ASTERISK_USERNAME "asterisk"
145 /* Default mail command to mail voicemail. Change it with the
146 mailcmd= command in voicemail.conf */
147 #define SENDMAIL "/usr/sbin/sendmail -t"
149 #define INTRO "vm-intro"
151 #define MAXMSG 100
152 #define MAXMSGLIMIT 9999
154 #define BASEMAXINLINE 256
155 #define BASELINELEN 72
156 #define BASEMAXINLINE 256
157 #define eol "\r\n"
159 #define MAX_DATETIME_FORMAT 512
160 #define MAX_NUM_CID_CONTEXTS 10
162 #define VM_REVIEW (1 << 0)
163 #define VM_OPERATOR (1 << 1)
164 #define VM_SAYCID (1 << 2)
165 #define VM_SVMAIL (1 << 3)
166 #define VM_ENVELOPE (1 << 4)
167 #define VM_SAYDURATION (1 << 5)
168 #define VM_SKIPAFTERCMD (1 << 6)
169 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
170 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
171 #define VM_PBXSKIP (1 << 9)
172 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
173 #define VM_ATTACH (1 << 11)
174 #define VM_DELETE (1 << 12)
175 #define VM_ALLOCED (1 << 13)
176 #define VM_SEARCH (1 << 14)
177 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
178 #define ERROR_LOCK_PATH -100
181 enum {
182 OPT_SILENT = (1 << 0),
183 OPT_BUSY_GREETING = (1 << 1),
184 OPT_UNAVAIL_GREETING = (1 << 2),
185 OPT_RECORDGAIN = (1 << 3),
186 OPT_PREPEND_MAILBOX = (1 << 4),
187 OPT_PRIORITY_JUMP = (1 << 5),
188 OPT_AUTOPLAY = (1 << 6),
189 } vm_option_flags;
191 enum {
192 OPT_ARG_RECORDGAIN = 0,
193 OPT_ARG_PLAYFOLDER = 1,
194 /* This *must* be the last value in this enum! */
195 OPT_ARG_ARRAY_SIZE = 2,
196 } vm_option_args;
198 AST_APP_OPTIONS(vm_app_options, {
199 AST_APP_OPTION('s', OPT_SILENT),
200 AST_APP_OPTION('b', OPT_BUSY_GREETING),
201 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
202 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
203 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
204 AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
205 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
208 static int load_config(void);
210 /*! \page vmlang Voicemail Language Syntaxes Supported
212 \par Syntaxes supported, not really language codes.
213 \arg \b en - English
214 \arg \b de - German
215 \arg \b es - Spanish
216 \arg \b fr - French
217 \arg \b it = Italian
218 \arg \b nl - Dutch
219 \arg \b pt - Polish
220 \arg \b pt - Portuguese
221 \arg \b gr - Greek
222 \arg \b no - Norwegian
223 \arg \b se - Swedish
225 German requires the following additional soundfile:
226 \arg \b 1F einE (feminine)
228 Spanish requires the following additional soundfile:
229 \arg \b 1M un (masculine)
231 Dutch, Portuguese & Spanish require the following additional soundfiles:
232 \arg \b vm-INBOXs singular of 'new'
233 \arg \b vm-Olds singular of 'old/heard/read'
235 NB these are plural:
236 \arg \b vm-INBOX nieuwe (nl)
237 \arg \b vm-Old oude (nl)
239 Polish uses:
240 \arg \b vm-new-a 'new', feminine singular accusative
241 \arg \b vm-new-e 'new', feminine plural accusative
242 \arg \b vm-new-ych 'new', feminine plural genitive
243 \arg \b vm-old-a 'old', feminine singular accusative
244 \arg \b vm-old-e 'old', feminine plural accusative
245 \arg \b vm-old-ych 'old', feminine plural genitive
246 \arg \b digits/1-a 'one', not always same as 'digits/1'
247 \arg \b digits/2-ie 'two', not always same as 'digits/2'
249 Swedish uses:
250 \arg \b vm-nytt singular of 'new'
251 \arg \b vm-nya plural of 'new'
252 \arg \b vm-gammalt singular of 'old'
253 \arg \b vm-gamla plural of 'old'
254 \arg \b digits/ett 'one', not always same as 'digits/1'
256 Norwegian uses:
257 \arg \b vm-ny singular of 'new'
258 \arg \b vm-nye plural of 'new'
259 \arg \b vm-gammel singular of 'old'
260 \arg \b vm-gamle plural of 'old'
262 Dutch also uses:
263 \arg \b nl-om 'at'?
265 Spanish also uses:
266 \arg \b vm-youhaveno
268 Italian requires the following additional soundfile:
270 For vm_intro_it:
271 \arg \b vm-nuovo new
272 \arg \b vm-nuovi new plural
273 \arg \b vm-vecchio old
274 \arg \b vm-vecchi old plural
276 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
277 spelled among others when you have to change folder. For the above reasons, vm-INBOX
278 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
282 struct baseio {
283 int iocp;
284 int iolen;
285 int linelength;
286 int ateof;
287 unsigned char iobuf[BASEMAXINLINE];
290 /*! Structure for linked list of users */
291 struct ast_vm_user {
292 char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
293 char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
294 char password[80]; /*!< Secret pin code, numbers only */
295 char fullname[80]; /*!< Full name, for directory app */
296 char email[80]; /*!< E-mail address */
297 char pager[80]; /*!< E-mail address to pager (no attachment) */
298 char serveremail[80]; /*!< From: Mail address */
299 char mailcmd[160]; /*!< Configurable mail command */
300 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
301 char zonetag[80]; /*!< Time zone */
302 char callback[80];
303 char dialout[80];
304 char uniqueid[20]; /*!< Unique integer identifier */
305 char exit[80];
306 char attachfmt[20]; /*!< Attachment format */
307 unsigned int flags; /*!< VM_ flags */
308 int saydurationm;
309 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
310 #ifdef IMAP_STORAGE
311 char imapuser[80]; /* IMAP server login */
312 #endif
313 double volgain; /*!< Volume gain for voicemails sent via email */
314 AST_LIST_ENTRY(ast_vm_user) list;
317 struct vm_zone {
318 AST_LIST_ENTRY(vm_zone) list;
319 char name[80];
320 char timezone[80];
321 char msg_format[512];
324 struct vm_state {
325 char curbox[80];
326 char username[80];
327 char curdir[256];
328 char vmbox[256];
329 char fn[256];
330 char fn2[256];
331 int *deleted;
332 int *heard;
333 int curmsg;
334 int lastmsg;
335 int newmessages;
336 int oldmessages;
337 int starting;
338 int repeats;
339 #ifdef IMAP_STORAGE
340 int updated; /* decremented on each mail check until 1 -allows delay */
341 long msgArray[256];
342 MAILSTREAM *mailstream;
343 int vmArrayIndex;
344 char imapuser[80]; /* IMAP server login */
345 int interactive;
346 unsigned int quota_limit;
347 unsigned int quota_usage;
348 struct vm_state *persist_vms;
349 #endif
351 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);
352 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
353 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
354 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
355 signed char record_gain);
356 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
357 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
358 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
359 #ifndef ODBC_STORAGE
360 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
361 #endif
362 static void apply_options(struct ast_vm_user *vmu, const char *options);
364 #ifdef ODBC_STORAGE
365 static char odbc_database[80];
366 static char odbc_table[80];
367 #define RETRIEVE(a,b) retrieve_file(a,b)
368 #define DISPOSE(a,b) remove_file(a,b)
369 #define STORE(a,b,c,d) store_file(a,b,c,d)
370 #define EXISTS(a,b,c,d) (message_exists(a,b))
371 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
372 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
373 #define DELETE(a,b,c) (delete_file(a,b))
374 #else
375 #ifdef IMAP_STORAGE
376 #define RETRIEVE(a,b)
377 #define DISPOSE(a,b)
378 #define STORE(a,b,c,d)
379 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
380 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
381 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
382 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
383 #define DELETE(a,b,c) (vm_delete(c))
384 #else
385 #define RETRIEVE(a,b)
386 #define DISPOSE(a,b)
387 #define STORE(a,b,c,d)
388 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
389 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
390 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
391 #define DELETE(a,b,c) (vm_delete(c))
392 #endif
393 #endif
395 static char VM_SPOOL_DIR[PATH_MAX];
397 static char ext_pass_cmd[128];
399 #if ODBC_STORAGE
400 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
401 #elif IMAP_STORAGE
402 #define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
403 #else
404 #define tdesc "Comedian Mail (Voicemail System)"
405 #endif
407 static char userscontext[AST_MAX_EXTENSION] = "default";
409 static char *addesc = "Comedian Mail";
411 static char *synopsis_vm =
412 "Leave a Voicemail message";
414 static char *descrip_vm =
415 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
416 "application allows the calling party to leave a message for the specified\n"
417 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
418 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
419 "specified mailbox does not exist.\n"
420 " The Voicemail application will exit if any of the following DTMF digits are\n"
421 "received:\n"
422 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
423 " * - Jump to the 'a' extension in the current dialplan context.\n"
424 " This application will set the following channel variable upon completion:\n"
425 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
426 " application. The possible values are:\n"
427 " SUCCESS | USEREXIT | FAILED\n\n"
428 " Options:\n"
429 " b - Play the 'busy' greeting to the calling party.\n"
430 " g(#) - Use the specified amount of gain when recording the voicemail\n"
431 " message. The units are whole-number decibels (dB).\n"
432 " s - Skip the playback of instructions for leaving a message to the\n"
433 " calling party.\n"
434 " u - Play the 'unavailble greeting.\n"
435 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
436 " error occurs.\n";
438 static char *synopsis_vmain =
439 "Check Voicemail messages";
441 static char *descrip_vmain =
442 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
443 "calling party to check voicemail messages. A specific mailbox, and optional\n"
444 "corresponding context, may be specified. If a mailbox is not provided, the\n"
445 "calling party will be prompted to enter one. If a context is not specified,\n"
446 "the 'default' context will be used.\n\n"
447 " Options:\n"
448 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
449 " is entered by the caller.\n"
450 " g(#) - Use the specified amount of gain when recording a voicemail\n"
451 " message. The units are whole-number decibels (dB).\n"
452 " s - Skip checking the passcode for the mailbox.\n"
453 " a(#) - Skip folder prompt and go directly to folder specified.\n"
454 " Defaults to INBOX\n";
456 static char *synopsis_vm_box_exists =
457 "Check to see if Voicemail mailbox exists";
459 static char *descrip_vm_box_exists =
460 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
461 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
462 "will be used.\n"
463 " This application will set the following channel variable upon completion:\n"
464 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
465 " MailboxExists application. Possible values include:\n"
466 " SUCCESS | FAILED\n\n"
467 " Options:\n"
468 " j - Jump to priority n+101 if the mailbox is found.\n";
470 static char *synopsis_vmauthenticate =
471 "Authenticate with Voicemail passwords";
473 static char *descrip_vmauthenticate =
474 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
475 "same way as the Authenticate application, but the passwords are taken from\n"
476 "voicemail.conf.\n"
477 " If the mailbox is specified, only that mailbox's password will be considered\n"
478 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
479 "be set with the authenticated mailbox.\n\n"
480 " Options:\n"
481 " s - Skip playing the initial prompts.\n";
483 /* Leave a message */
484 static char *app = "VoiceMail";
486 /* Check mail, control, etc */
487 static char *app2 = "VoiceMailMain";
489 static char *app3 = "MailboxExists";
490 static char *app4 = "VMAuthenticate";
492 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
493 static AST_LIST_HEAD_STATIC(zones, vm_zone);
494 static int maxsilence;
495 static int maxmsg;
496 static int silencethreshold = 128;
497 static char serveremail[80];
498 static char mailcmd[160]; /* Configurable mail cmd */
499 static char externnotify[160];
500 static struct ast_smdi_interface *smdi_iface = NULL;
501 static char vmfmts[80];
502 static double volgain;
503 static int vmminmessage;
504 static int vmmaxmessage;
505 static int maxgreet;
506 static int skipms;
507 static int maxlogins;
509 static struct ast_flags globalflags = {0};
511 static int saydurationminfo;
513 static char dialcontext[AST_MAX_CONTEXT];
514 static char callcontext[AST_MAX_CONTEXT];
515 static char exitcontext[AST_MAX_CONTEXT];
517 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
520 static char *emailbody = NULL;
521 static char *emailsubject = NULL;
522 static char *pagerbody = NULL;
523 static char *pagersubject = NULL;
524 static char fromstring[100];
525 static char pagerfromstring[100];
526 static char emailtitle[100];
527 static char charset[32] = "ISO-8859-1";
529 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
530 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
531 static int adsiver = 1;
532 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
535 static void populate_defaults(struct ast_vm_user *vmu)
537 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
538 if (saydurationminfo)
539 vmu->saydurationm = saydurationminfo;
540 if (callcontext)
541 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
542 if (dialcontext)
543 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
544 if (exitcontext)
545 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
546 if (maxmsg)
547 vmu->maxmsg = maxmsg;
548 vmu->volgain = volgain;
551 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
553 int x;
554 if (!strcasecmp(var, "attach")) {
555 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
556 } else if (!strcasecmp(var, "attachfmt")) {
557 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
558 } else if (!strcasecmp(var, "serveremail")) {
559 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
560 } else if (!strcasecmp(var, "language")) {
561 ast_copy_string(vmu->language, value, sizeof(vmu->language));
562 } else if (!strcasecmp(var, "tz")) {
563 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
564 #ifdef IMAP_STORAGE
565 } else if (!strcasecmp(var, "imapuser")) {
566 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
567 #endif
568 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
569 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
570 } else if (!strcasecmp(var, "saycid")){
571 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
572 } else if (!strcasecmp(var,"sendvoicemail")){
573 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
574 } else if (!strcasecmp(var, "review")){
575 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
576 } else if (!strcasecmp(var, "tempgreetwarn")){
577 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
578 } else if (!strcasecmp(var, "operator")){
579 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
580 } else if (!strcasecmp(var, "envelope")){
581 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
582 } else if (!strcasecmp(var, "sayduration")){
583 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
584 } else if (!strcasecmp(var, "saydurationm")){
585 if (sscanf(value, "%d", &x) == 1) {
586 vmu->saydurationm = x;
587 } else {
588 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
590 } else if (!strcasecmp(var, "forcename")){
591 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
592 } else if (!strcasecmp(var, "forcegreetings")){
593 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
594 } else if (!strcasecmp(var, "callback")) {
595 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
596 } else if (!strcasecmp(var, "dialout")) {
597 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
598 } else if (!strcasecmp(var, "exitcontext")) {
599 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
600 } else if (!strcasecmp(var, "maxmsg")) {
601 vmu->maxmsg = atoi(value);
602 if (vmu->maxmsg <= 0) {
603 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
604 vmu->maxmsg = MAXMSG;
605 } else if (vmu->maxmsg > MAXMSGLIMIT) {
606 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
607 vmu->maxmsg = MAXMSGLIMIT;
609 } else if (!strcasecmp(var, "volgain")) {
610 sscanf(value, "%lf", &vmu->volgain);
611 } else if (!strcasecmp(var, "options")) {
612 apply_options(vmu, value);
616 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
618 int res;
619 if (!ast_strlen_zero(vmu->uniqueid)) {
620 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
621 if (res > 0) {
622 ast_copy_string(vmu->password, password, sizeof(vmu->password));
623 res = 0;
624 } else if (!res) {
625 res = -1;
627 return res;
629 return -1;
632 static void apply_options(struct ast_vm_user *vmu, const char *options)
633 { /* Destructively Parse options and apply */
634 char *stringp;
635 char *s;
636 char *var, *value;
637 stringp = ast_strdupa(options);
638 while ((s = strsep(&stringp, "|"))) {
639 value = s;
640 if ((var = strsep(&value, "=")) && value) {
641 apply_option(vmu, var, value);
646 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
648 struct ast_variable *tmp;
649 tmp = var;
650 while (tmp) {
651 if (!strcasecmp(tmp->name, "password")) {
652 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
653 } else if (!strcasecmp(tmp->name, "uniqueid")) {
654 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
655 } else if (!strcasecmp(tmp->name, "pager")) {
656 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
657 } else if (!strcasecmp(tmp->name, "email")) {
658 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
659 } else if (!strcasecmp(tmp->name, "fullname")) {
660 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
661 } else if (!strcasecmp(tmp->name, "context")) {
662 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
663 } else
664 apply_option(retval, tmp->name, tmp->value);
665 tmp = tmp->next;
669 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
671 struct ast_variable *var;
672 struct ast_vm_user *retval;
674 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
675 if (!ivm)
676 ast_set_flag(retval, VM_ALLOCED);
677 else
678 memset(retval, 0, sizeof(*retval));
679 if (mailbox)
680 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
681 populate_defaults(retval);
682 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
683 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
684 else
685 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
686 if (var) {
687 apply_options_full(retval, var);
688 ast_variables_destroy(var);
689 } else {
690 if (!ivm)
691 free(retval);
692 retval = NULL;
695 return retval;
698 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
700 /* This function could be made to generate one from a database, too */
701 struct ast_vm_user *vmu=NULL, *cur;
702 AST_LIST_LOCK(&users);
704 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
705 context = "default";
707 AST_LIST_TRAVERSE(&users, cur, list) {
708 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
709 break;
710 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
711 break;
713 if (cur) {
714 /* Make a copy, so that on a reload, we have no race */
715 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
716 memcpy(vmu, cur, sizeof(*vmu));
717 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
718 AST_LIST_NEXT(vmu, list) = NULL;
720 } else
721 vmu = find_user_realtime(ivm, context, mailbox);
722 AST_LIST_UNLOCK(&users);
723 return vmu;
726 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
728 /* This function could be made to generate one from a database, too */
729 struct ast_vm_user *cur;
730 int res = -1;
731 AST_LIST_LOCK(&users);
732 AST_LIST_TRAVERSE(&users, cur, list) {
733 if ((!context || !strcasecmp(context, cur->context)) &&
734 (!strcasecmp(mailbox, cur->mailbox)))
735 break;
737 if (cur) {
738 ast_copy_string(cur->password, newpass, sizeof(cur->password));
739 res = 0;
741 AST_LIST_UNLOCK(&users);
742 return res;
745 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
747 /* There's probably a better way of doing this. */
748 /* That's why I've put the password change in a separate function. */
749 /* This could also be done with a database function */
751 FILE *configin;
752 FILE *configout;
753 int linenum=0;
754 char inbuf[256];
755 char orig[256];
756 char currcontext[256] ="";
757 char tmpin[PATH_MAX];
758 char tmpout[PATH_MAX];
759 struct stat statbuf;
761 if (!change_password_realtime(vmu, newpassword))
762 return;
764 snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
765 snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
766 configin = fopen(tmpin,"r");
767 if (configin)
768 configout = fopen(tmpout,"w+");
769 else
770 configout = NULL;
771 if (!configin || !configout) {
772 if (configin)
773 fclose(configin);
774 else
775 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
776 if (configout)
777 fclose(configout);
778 else
779 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
780 return;
783 while (!feof(configin)) {
784 char *user = NULL, *pass = NULL, *rest = NULL, *comment = NULL, *tmpctx = NULL, *tmpctxend = NULL;
786 /* Read in the line */
787 if (fgets(inbuf, sizeof(inbuf), configin) == NULL)
788 continue;
789 linenum++;
791 /* Make a backup of it */
792 ast_copy_string(orig, inbuf, sizeof(orig));
795 Read the file line by line, split each line into a comment and command section
796 only parse the command portion of the line
798 if (inbuf[strlen(inbuf) - 1] == '\n')
799 inbuf[strlen(inbuf) - 1] = '\0';
801 if ((comment = strchr(inbuf, ';')))
802 *comment++ = '\0'; /* Now inbuf is terminated just before the comment */
804 if (ast_strlen_zero(inbuf)) {
805 fprintf(configout, "%s", orig);
806 continue;
809 /* Check for a context, first '[' to first ']' */
810 if ((tmpctx = strchr(inbuf, '['))) {
811 tmpctxend = strchr(tmpctx, ']');
812 if (tmpctxend) {
813 /* Valid context */
814 ast_copy_string(currcontext, tmpctx + 1, tmpctxend - tmpctx);
815 fprintf(configout, "%s", orig);
816 continue;
820 /* This isn't a context line, check for MBX => PSWD... */
821 user = inbuf;
822 if ((pass = strchr(user, '='))) {
823 /* We have a line in the form of aaaaa=aaaaaa */
824 *pass++ = '\0';
826 user = ast_strip(user);
828 if (*pass == '>')
829 *pass++ = '\0';
831 pass = ast_skip_blanks(pass);
834 Since no whitespace allowed in fields, or more correctly white space
835 inside the fields is there for a purpose, we can just terminate pass
836 at the comma or EOL whichever comes first.
838 if ((rest = strchr(pass, ',')))
839 *rest++ = '\0';
840 } else {
841 user = NULL;
844 /* Compare user, pass AND context */
845 if (!ast_strlen_zero(user) && !strcmp(user, vmu->mailbox) &&
846 !ast_strlen_zero(pass) && !strcmp(pass, vmu->password) &&
847 !strcasecmp(currcontext, vmu->context)) {
848 /* This is the line */
849 if (rest) {
850 fprintf(configout, "%s => %s,%s", user, newpassword, rest);
851 } else {
852 fprintf(configout, "%s => %s", user, newpassword);
854 /* If there was a comment on the line print it out */
855 if (comment) {
856 fprintf(configout, ";%s\n", comment);
857 } else {
858 fprintf(configout, "\n");
860 } else {
861 /* Put it back like it was */
862 fprintf(configout, "%s", orig);
865 fclose(configin);
866 fclose(configout);
868 stat(tmpin, &statbuf);
869 chmod(tmpout, statbuf.st_mode);
870 chown(tmpout, statbuf.st_uid, statbuf.st_gid);
871 unlink(tmpin);
872 rename(tmpout, tmpin);
873 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
874 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
877 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
879 char buf[255];
880 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
881 if (!ast_safe_system(buf))
882 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
885 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
887 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
890 #ifdef IMAP_STORAGE
891 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num)
893 char gsmdir[256];
895 sprintf(gsmdir,"%s/%s",dir,imapuser);
896 if (mkdir(gsmdir, 01777) && (errno != EEXIST)) {
897 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", gsmdir, strerror(errno));
898 return sprintf(dest, "%s/msg%04d", dir, num);
900 /* return sprintf(dest, "%s/s/msg%04d", dir, imapuser, num); */
901 return sprintf(dest, "%s/%s/msg%04d", dir, imapuser, num);
904 static void vm_imap_delete(int msgnum, struct vm_state *vms)
906 unsigned long messageNum = 0;
907 char arg[10];
909 /* find real message number based on msgnum */
910 /* this may be an index into vms->msgArray based on the msgnum. */
912 messageNum = vms->msgArray[msgnum];
913 if (messageNum == 0) {
914 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
915 return;
917 if(option_debug > 2)
918 ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
919 /* delete message */
920 sprintf (arg,"%lu",messageNum);
921 mail_setflag (vms->mailstream,arg,"\\DELETED");
924 #endif
925 static int make_file(char *dest, int len, char *dir, int num)
927 return snprintf(dest, len, "%s/msg%04d", dir, num);
930 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
931 * \param dest String. base directory.
932 * \param context String. Ignored if is null or empty string.
933 * \param ext String. Ignored if is null or empty string.
934 * \param folder String. Ignored if is null or empty string.
935 * \return 0 on failure, 1 on success.
937 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
939 mode_t mode = VOICEMAIL_DIR_MODE;
941 if (!ast_strlen_zero(context)) {
942 make_dir(dest, len, context, "", "");
943 if (mkdir(dest, mode) && errno != EEXIST) {
944 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
945 return 0;
948 if (!ast_strlen_zero(ext)) {
949 make_dir(dest, len, context, ext, "");
950 if (mkdir(dest, mode) && errno != EEXIST) {
951 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
952 return 0;
955 if (!ast_strlen_zero(folder)) {
956 make_dir(dest, len, context, ext, folder);
957 if (mkdir(dest, mode) && errno != EEXIST) {
958 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
959 return 0;
962 return 1;
965 /* only return failure if ast_lock_path returns 'timeout',
966 not if the path does not exist or any other reason
968 static int vm_lock_path(const char *path)
970 switch (ast_lock_path(path)) {
971 case AST_LOCK_TIMEOUT:
972 return -1;
973 default:
974 return 0;
979 #ifdef ODBC_STORAGE
980 static int retrieve_file(char *dir, int msgnum)
982 int x = 0;
983 int res;
984 int fd=-1;
985 size_t fdlen = 0;
986 void *fdm=NULL;
987 SQLSMALLINT colcount=0;
988 SQLHSTMT stmt;
989 char sql[256];
990 char fmt[80]="";
991 char *c;
992 char coltitle[256];
993 SQLSMALLINT collen;
994 SQLSMALLINT datatype;
995 SQLSMALLINT decimaldigits;
996 SQLSMALLINT nullable;
997 SQLULEN colsize;
998 FILE *f=NULL;
999 char rowdata[80];
1000 char fn[256];
1001 char full_fn[256];
1002 char msgnums[80];
1004 struct odbc_obj *obj;
1005 obj = odbc_request_obj(odbc_database, 0);
1006 if (obj) {
1007 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1008 c = strchr(fmt, '|');
1009 if (c)
1010 *c = '\0';
1011 if (!strcasecmp(fmt, "wav49"))
1012 strcpy(fmt, "WAV");
1013 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1014 if (msgnum > -1)
1015 make_file(fn, sizeof(fn), dir, msgnum);
1016 else
1017 ast_copy_string(fn, dir, sizeof(fn));
1018 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1019 f = fopen(full_fn, "w+");
1020 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1021 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1022 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1023 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1024 odbc_release_obj(obj);
1025 goto yuck;
1027 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1028 res = SQLPrepare(stmt, sql, SQL_NTS);
1029 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1030 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1031 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1032 odbc_release_obj(obj);
1033 goto yuck;
1035 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1036 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1037 res = odbc_smart_execute(obj, stmt);
1038 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1039 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1040 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1041 odbc_release_obj(obj);
1042 goto yuck;
1044 res = SQLFetch(stmt);
1045 if (res == SQL_NO_DATA) {
1046 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1047 odbc_release_obj(obj);
1048 goto yuck;
1050 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1051 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1052 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1053 odbc_release_obj(obj);
1054 goto yuck;
1056 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, 0770);
1057 if (fd < 0) {
1058 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1059 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1060 odbc_release_obj(obj);
1061 goto yuck;
1063 res = SQLNumResultCols(stmt, &colcount);
1064 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1065 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1066 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1067 odbc_release_obj(obj);
1068 goto yuck;
1070 if (f)
1071 fprintf(f, "[message]\n");
1072 for (x=0;x<colcount;x++) {
1073 rowdata[0] = '\0';
1074 collen = sizeof(coltitle);
1075 res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen,
1076 &datatype, &colsize, &decimaldigits, &nullable);
1077 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1078 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1079 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1080 odbc_release_obj(obj);
1081 goto yuck;
1083 if (!strcasecmp(coltitle, "recording")) {
1084 res = SQLGetData(stmt, x + 1, SQL_BINARY, NULL, 0, &colsize);
1085 fdlen = colsize;
1086 if (fd > -1) {
1087 char tmp[1]="";
1088 lseek(fd, fdlen - 1, SEEK_SET);
1089 if (write(fd, tmp, 1) != 1) {
1090 close(fd);
1091 fd = -1;
1092 continue;
1094 if (fd > -1)
1095 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1097 if (fdm) {
1098 memset(fdm, 0, fdlen);
1099 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
1100 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1101 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1102 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1103 odbc_release_obj(obj);
1104 goto yuck;
1107 } else {
1108 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1109 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1110 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1111 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1112 odbc_release_obj(obj);
1113 goto yuck;
1115 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1116 fprintf(f, "%s=%s\n", coltitle, rowdata);
1119 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1120 odbc_release_obj(obj);
1121 } else
1122 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1123 yuck:
1124 if (f)
1125 fclose(f);
1126 if (fdm)
1127 munmap(fdm, fdlen);
1128 if (fd > -1)
1129 close(fd);
1130 return x - 1;
1133 static int remove_file(char *dir, int msgnum)
1135 char fn[256];
1136 char full_fn[256];
1137 char msgnums[80];
1139 if (msgnum > -1) {
1140 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1141 make_file(fn, sizeof(fn), dir, msgnum);
1142 } else
1143 ast_copy_string(fn, dir, sizeof(fn));
1144 ast_filedelete(fn, NULL);
1145 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1146 unlink(full_fn);
1147 return 0;
1150 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1152 int x = 0;
1153 int res;
1154 SQLHSTMT stmt;
1155 char sql[256];
1156 char rowdata[20];
1158 struct odbc_obj *obj;
1159 obj = odbc_request_obj(odbc_database, 0);
1160 if (obj) {
1161 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1162 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1163 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1164 odbc_release_obj(obj);
1165 goto yuck;
1167 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1168 res = SQLPrepare(stmt, sql, SQL_NTS);
1169 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1170 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1171 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1172 odbc_release_obj(obj);
1173 goto yuck;
1175 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1176 res = odbc_smart_execute(obj, stmt);
1177 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1178 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1179 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1180 odbc_release_obj(obj);
1181 goto yuck;
1183 res = SQLFetch(stmt);
1184 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1185 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1186 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1187 odbc_release_obj(obj);
1188 goto yuck;
1190 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1191 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1192 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1193 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1194 odbc_release_obj(obj);
1195 goto yuck;
1197 if (sscanf(rowdata, "%d", &x) != 1)
1198 ast_log(LOG_WARNING, "Failed to read message count!\n");
1199 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1200 odbc_release_obj(obj);
1201 } else
1202 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1203 yuck:
1204 return x - 1;
1207 static int message_exists(char *dir, int msgnum)
1209 int x = 0;
1210 int res;
1211 SQLHSTMT stmt;
1212 char sql[256];
1213 char rowdata[20];
1214 char msgnums[20];
1216 struct odbc_obj *obj;
1217 obj = odbc_request_obj(odbc_database, 0);
1218 if (obj) {
1219 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1220 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1221 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1222 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1223 odbc_release_obj(obj);
1224 goto yuck;
1226 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1227 res = SQLPrepare(stmt, sql, SQL_NTS);
1228 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1229 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1230 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1231 odbc_release_obj(obj);
1232 goto yuck;
1234 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1235 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1236 res = odbc_smart_execute(obj, stmt);
1237 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1238 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1239 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1240 odbc_release_obj(obj);
1241 goto yuck;
1243 res = SQLFetch(stmt);
1244 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1245 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1246 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1247 odbc_release_obj(obj);
1248 goto yuck;
1250 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1251 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1252 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1253 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1254 odbc_release_obj(obj);
1255 goto yuck;
1257 if (sscanf(rowdata, "%d", &x) != 1)
1258 ast_log(LOG_WARNING, "Failed to read message count!\n");
1259 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1260 odbc_release_obj(obj);
1261 } else
1262 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1263 yuck:
1264 return x;
1267 static int count_messages(struct ast_vm_user *vmu, char *dir)
1269 return last_message_index(vmu, dir) + 1;
1272 static void delete_file(char *sdir, int smsg)
1274 int res;
1275 SQLHSTMT stmt;
1276 char sql[256];
1277 char msgnums[20];
1279 struct odbc_obj *obj;
1280 obj = odbc_request_obj(odbc_database, 0);
1281 if (obj) {
1282 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1283 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1284 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1285 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1286 odbc_release_obj(obj);
1287 goto yuck;
1289 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1290 res = SQLPrepare(stmt, sql, SQL_NTS);
1291 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1292 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1293 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1294 odbc_release_obj(obj);
1295 goto yuck;
1297 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1298 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1299 res = odbc_smart_execute(obj, stmt);
1300 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1301 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1302 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1303 odbc_release_obj(obj);
1304 goto yuck;
1306 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1307 odbc_release_obj(obj);
1308 } else
1309 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1310 yuck:
1311 return;
1314 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1316 int res;
1317 SQLHSTMT stmt;
1318 char sql[512];
1319 char msgnums[20];
1320 char msgnumd[20];
1321 struct odbc_obj *obj;
1323 delete_file(ddir, dmsg);
1324 obj = odbc_request_obj(odbc_database, 0);
1325 if (obj) {
1326 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1327 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1328 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1329 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1330 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1331 odbc_release_obj(obj);
1332 goto yuck;
1334 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);
1335 res = SQLPrepare(stmt, sql, SQL_NTS);
1336 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1337 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1338 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1339 odbc_release_obj(obj);
1340 goto yuck;
1342 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1343 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1344 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1345 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1346 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1347 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1348 res = odbc_smart_execute(obj, stmt);
1349 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1350 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1351 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1352 odbc_release_obj(obj);
1353 goto yuck;
1355 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1356 odbc_release_obj(obj);
1357 } else
1358 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1359 yuck:
1360 return;
1363 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1365 int x = 0;
1366 int res;
1367 int fd = -1;
1368 void *fdm=NULL;
1369 size_t fdlen = -1;
1370 SQLHSTMT stmt;
1371 SQLINTEGER len;
1372 char sql[256];
1373 char msgnums[20];
1374 char fn[256];
1375 char full_fn[256];
1376 char fmt[80]="";
1377 char *c;
1378 char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1379 char *category = "";
1380 struct ast_config *cfg=NULL;
1381 struct odbc_obj *obj;
1383 delete_file(dir, msgnum);
1384 obj = odbc_request_obj(odbc_database, 0);
1385 if (obj) {
1386 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1387 c = strchr(fmt, '|');
1388 if (c)
1389 *c = '\0';
1390 if (!strcasecmp(fmt, "wav49"))
1391 strcpy(fmt, "WAV");
1392 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1393 if (msgnum > -1)
1394 make_file(fn, sizeof(fn), dir, msgnum);
1395 else
1396 ast_copy_string(fn, dir, sizeof(fn));
1397 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1398 cfg = ast_config_load(full_fn);
1399 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1400 fd = open(full_fn, O_RDWR);
1401 if (fd < 0) {
1402 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1403 odbc_release_obj(obj);
1404 goto yuck;
1406 if (cfg) {
1407 context = ast_variable_retrieve(cfg, "message", "context");
1408 if (!context) context = "";
1409 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1410 if (!macrocontext) macrocontext = "";
1411 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1412 if (!callerid) callerid = "";
1413 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1414 if (!origtime) origtime = "";
1415 duration = ast_variable_retrieve(cfg, "message", "duration");
1416 if (!duration) duration = "";
1417 category = ast_variable_retrieve(cfg, "message", "category");
1418 if (!category) category = "";
1420 fdlen = lseek(fd, 0, SEEK_END);
1421 lseek(fd, 0, SEEK_SET);
1422 printf("Length is %zd\n", fdlen);
1423 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1424 if (!fdm) {
1425 ast_log(LOG_WARNING, "Memory map failed!\n");
1426 odbc_release_obj(obj);
1427 goto yuck;
1429 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1430 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1431 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1432 odbc_release_obj(obj);
1433 goto yuck;
1435 if (!ast_strlen_zero(category))
1436 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1437 else
1438 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1439 res = SQLPrepare(stmt, sql, SQL_NTS);
1440 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1441 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1442 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1443 odbc_release_obj(obj);
1444 goto yuck;
1446 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1447 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1448 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1449 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1450 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1451 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1452 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1453 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1454 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1455 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1456 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1457 if (!ast_strlen_zero(category))
1458 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1459 res = odbc_smart_execute(obj, stmt);
1460 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1461 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1462 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1463 odbc_release_obj(obj);
1464 goto yuck;
1466 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1467 odbc_release_obj(obj);
1468 } else
1469 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1470 yuck:
1471 if (cfg)
1472 ast_config_destroy(cfg);
1473 if (fdm)
1474 munmap(fdm, fdlen);
1475 if (fd > -1)
1476 close(fd);
1477 return x;
1480 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1482 int res;
1483 SQLHSTMT stmt;
1484 char sql[256];
1485 char msgnums[20];
1486 char msgnumd[20];
1487 struct odbc_obj *obj;
1489 delete_file(ddir, dmsg);
1490 obj = odbc_request_obj(odbc_database, 0);
1491 if (obj) {
1492 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1493 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1494 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1495 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1496 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1497 odbc_release_obj(obj);
1498 goto yuck;
1500 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1501 res = SQLPrepare(stmt, sql, SQL_NTS);
1502 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1503 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1504 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1505 odbc_release_obj(obj);
1506 goto yuck;
1508 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1509 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1510 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1511 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1512 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1513 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1514 res = odbc_smart_execute(obj, stmt);
1515 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1516 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1517 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1518 odbc_release_obj(obj);
1519 goto yuck;
1521 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1522 odbc_release_obj(obj);
1523 } else
1524 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1525 yuck:
1526 return;
1529 #else
1530 static int count_messages(struct ast_vm_user *vmu, char *dir)
1532 /* Find all .txt files - even if they are not in sequence from 0000 */
1534 int vmcount = 0;
1535 DIR *vmdir = NULL;
1536 struct dirent *vment = NULL;
1538 if (vm_lock_path(dir))
1539 return ERROR_LOCK_PATH;
1541 if ((vmdir = opendir(dir))) {
1542 while ((vment = readdir(vmdir))) {
1543 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1544 vmcount++;
1546 closedir(vmdir);
1548 ast_unlock_path(dir);
1550 return vmcount;
1553 static void rename_file(char *sfn, char *dfn)
1555 char stxt[256];
1556 char dtxt[256];
1557 ast_filerename(sfn,dfn,NULL);
1558 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1559 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1560 rename(stxt, dtxt);
1563 static int copy(char *infile, char *outfile)
1565 int ifd;
1566 int ofd;
1567 int res;
1568 int len;
1569 char buf[4096];
1571 #ifdef HARDLINK_WHEN_POSSIBLE
1572 /* Hard link if possible; saves disk space & is faster */
1573 if (link(infile, outfile)) {
1574 #endif
1575 if ((ifd = open(infile, O_RDONLY)) < 0) {
1576 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1577 return -1;
1579 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1580 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1581 close(ifd);
1582 return -1;
1584 do {
1585 len = read(ifd, buf, sizeof(buf));
1586 if (len < 0) {
1587 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1588 close(ifd);
1589 close(ofd);
1590 unlink(outfile);
1592 if (len) {
1593 res = write(ofd, buf, len);
1594 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1595 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1596 close(ifd);
1597 close(ofd);
1598 unlink(outfile);
1601 } while (len);
1602 close(ifd);
1603 close(ofd);
1604 return 0;
1605 #ifdef HARDLINK_WHEN_POSSIBLE
1606 } else {
1607 /* Hard link succeeded */
1608 return 0;
1610 #endif
1613 static void copy_file(char *frompath, char *topath)
1615 char frompath2[256],topath2[256];
1616 ast_filecopy(frompath, topath, NULL);
1617 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1618 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1619 copy(frompath2, topath2);
1623 * A negative return value indicates an error.
1625 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1627 int x;
1628 char fn[256];
1630 if (vm_lock_path(dir))
1631 return ERROR_LOCK_PATH;
1633 for (x = 0; x < vmu->maxmsg; x++) {
1634 make_file(fn, sizeof(fn), dir, x);
1635 if (ast_fileexists(fn, NULL, NULL) < 1)
1636 break;
1638 ast_unlock_path(dir);
1640 return x - 1;
1643 static int vm_delete(char *file)
1645 char *txt;
1646 int txtsize = 0;
1648 txtsize = (strlen(file) + 5)*sizeof(char);
1649 txt = alloca(txtsize);
1650 /* Sprintf here would safe because we alloca'd exactly the right length,
1651 * but trying to eliminate all sprintf's anyhow
1653 snprintf(txt, txtsize, "%s.txt", file);
1654 unlink(txt);
1655 return ast_filedelete(file, NULL);
1659 #endif
1660 static int inbuf(struct baseio *bio, FILE *fi)
1662 int l;
1664 if (bio->ateof)
1665 return 0;
1667 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1668 if (ferror(fi))
1669 return -1;
1671 bio->ateof = 1;
1672 return 0;
1675 bio->iolen= l;
1676 bio->iocp= 0;
1678 return 1;
1681 static int inchar(struct baseio *bio, FILE *fi)
1683 if (bio->iocp>=bio->iolen) {
1684 if (!inbuf(bio, fi))
1685 return EOF;
1688 return bio->iobuf[bio->iocp++];
1691 static int ochar(struct baseio *bio, int c, FILE *so)
1693 if (bio->linelength>=BASELINELEN) {
1694 if (fputs(eol,so)==EOF)
1695 return -1;
1697 bio->linelength= 0;
1700 if (putc(((unsigned char)c),so)==EOF)
1701 return -1;
1703 bio->linelength++;
1705 return 1;
1708 static int base_encode(char *filename, FILE *so)
1710 unsigned char dtable[BASEMAXINLINE];
1711 int i,hiteof= 0;
1712 FILE *fi;
1713 struct baseio bio;
1715 memset(&bio, 0, sizeof(bio));
1716 bio.iocp = BASEMAXINLINE;
1718 if (!(fi = fopen(filename, "rb"))) {
1719 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1720 return -1;
1723 for (i= 0;i<9;i++) {
1724 dtable[i]= 'A'+i;
1725 dtable[i+9]= 'J'+i;
1726 dtable[26+i]= 'a'+i;
1727 dtable[26+i+9]= 'j'+i;
1729 for (i= 0;i<8;i++) {
1730 dtable[i+18]= 'S'+i;
1731 dtable[26+i+18]= 's'+i;
1733 for (i= 0;i<10;i++) {
1734 dtable[52+i]= '0'+i;
1736 dtable[62]= '+';
1737 dtable[63]= '/';
1739 while (!hiteof){
1740 unsigned char igroup[3],ogroup[4];
1741 int c,n;
1743 igroup[0]= igroup[1]= igroup[2]= 0;
1745 for (n= 0;n<3;n++) {
1746 if ((c = inchar(&bio, fi)) == EOF) {
1747 hiteof= 1;
1748 break;
1751 igroup[n]= (unsigned char)c;
1754 if (n> 0) {
1755 ogroup[0]= dtable[igroup[0]>>2];
1756 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1757 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1758 ogroup[3]= dtable[igroup[2]&0x3F];
1760 if (n<3) {
1761 ogroup[3]= '=';
1763 if (n<2)
1764 ogroup[2]= '=';
1767 for (i= 0;i<4;i++)
1768 ochar(&bio, ogroup[i], so);
1772 if (fputs(eol,so)==EOF)
1773 return 0;
1775 fclose(fi);
1777 return 1;
1780 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)
1782 char callerid[256];
1783 /* Prepare variables for substition in email body and subject */
1784 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1785 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1786 snprintf(passdata, passdatasize, "%d", msgnum);
1787 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1788 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1789 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1790 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1791 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1792 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1793 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1794 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1798 * fill in *tm for current time according to the proper timezone, if any.
1799 * Return tm so it can be used as a function argument.
1801 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1803 const struct vm_zone *z = NULL;
1804 time_t t = time(NULL);
1806 /* Does this user have a timezone specified? */
1807 if (!ast_strlen_zero(vmu->zonetag)) {
1808 /* Find the zone in the list */
1809 AST_LIST_LOCK(&zones);
1810 AST_LIST_TRAVERSE(&zones, z, list) {
1811 if (!strcmp(z->name, vmu->zonetag))
1812 break;
1814 AST_LIST_UNLOCK(&zones);
1816 ast_localtime(&t, tm, z ? z->timezone : NULL);
1817 return tm;
1820 /* same as mkstemp, but return a FILE * */
1821 static FILE *vm_mkftemp(char *template)
1823 FILE *p = NULL;
1824 int pfd = mkstemp(template);
1825 if (pfd > -1) {
1826 p = fdopen(pfd, "w");
1827 if (!p) {
1828 close(pfd);
1829 pfd = -1;
1832 return p;
1835 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)
1837 FILE *p=NULL;
1838 char date[256];
1839 char host[MAXHOSTNAMELEN] = "";
1840 char who[256];
1841 char bound[256];
1842 char fname[256];
1843 char dur[256];
1844 char tmp[80] = "/tmp/astmail-XXXXXX";
1845 char tmp2[256];
1846 char tmpcmd[256];
1847 struct tm tm;
1849 if (vmu && ast_strlen_zero(vmu->email)) {
1850 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
1851 return(0);
1853 if (!strcmp(format, "wav49"))
1854 format = "WAV";
1855 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));
1856 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1857 command hangs */
1858 if ((p = vm_mkftemp(tmp)) == NULL) {
1859 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1860 return -1;
1861 } else {
1862 gethostname(host, sizeof(host)-1);
1863 if (strchr(srcemail, '@'))
1864 ast_copy_string(who, srcemail, sizeof(who));
1865 else {
1866 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1868 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1869 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1870 fprintf(p, "Date: %s\n", date);
1872 /* Set date format for voicemail mail */
1873 strftime(date, sizeof(date), emaildateformat, &tm);
1875 if (*fromstring) {
1876 struct ast_channel *ast;
1877 if ((ast = ast_channel_alloc(0))) {
1878 char *passdata;
1879 int vmlen = strlen(fromstring)*3 + 200;
1880 if ((passdata = alloca(vmlen))) {
1881 memset(passdata, 0, vmlen);
1882 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1883 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1884 fprintf(p, "From: %s <%s>\n",passdata,who);
1885 } else
1886 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1887 ast_channel_free(ast);
1888 } else
1889 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1890 } else
1891 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1892 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
1894 if (emailsubject) {
1895 struct ast_channel *ast;
1896 if ((ast = ast_channel_alloc(0))) {
1897 char *passdata;
1898 int vmlen = strlen(emailsubject)*3 + 200;
1899 if ((passdata = alloca(vmlen))) {
1900 memset(passdata, 0, vmlen);
1901 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1902 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
1903 fprintf(p, "Subject: %s\n", passdata);
1904 } else
1905 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1906 ast_channel_free(ast);
1907 } else
1908 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1909 } else
1910 if (*emailtitle) {
1911 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1912 fprintf(p,"\n") ;
1913 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1914 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1915 else
1916 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1917 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)ast_random(), mailbox, getpid(), host);
1918 #ifdef IMAP_STORAGE
1919 /* additional information needed for IMAP searching */
1920 fprintf(p, "X-Asterisk-VM-Message-Num: %d\n", msgnum + 1);
1921 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s\n", ext); */
1922 fprintf(p, "X-Asterisk-VM-Server-Name: %s\n", fromstring);
1923 fprintf(p, "X-Asterisk-VM-Context: %s\n", context);
1924 fprintf(p, "X-Asterisk-VM-Extension: %s\n", chan->exten);
1925 fprintf(p, "X-Asterisk-VM-Priority: %d\n", chan->priority);
1926 fprintf(p, "X-Asterisk-VM-Caller-channel: %s\n", chan->name);
1927 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s\n", cidnum);
1928 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s\n", cidname);
1929 fprintf(p, "X-Asterisk-VM-Duration: %d\n", duration);
1930 if (!ast_strlen_zero(category))
1931 fprintf(p, "X-Asterisk-VM-Category: %s\n", category);
1932 fprintf(p, "X-Asterisk-VM-Orig-date: %s\n", date);
1933 fprintf(p, "X-Asterisk-VM-Orig-time: %ld\n", (long)time(NULL));
1934 #endif
1935 if (!ast_strlen_zero(cidnum))
1936 fprintf(p, "X-Asterisk-CallerID: %s\n", cidnum);
1937 if (!ast_strlen_zero(cidname))
1938 fprintf(p, "X-Asterisk-CallerIDName: %s\n", cidname);
1939 fprintf(p, "MIME-Version: 1.0\n");
1940 if (attach_user_voicemail) {
1941 /* Something unique. */
1942 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)ast_random());
1944 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1946 fprintf(p, "--%s\n", bound);
1948 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
1949 if (emailbody) {
1950 struct ast_channel *ast;
1951 if ((ast = ast_channel_alloc(0))) {
1952 char *passdata;
1953 int vmlen = strlen(emailbody)*3 + 200;
1954 if ((passdata = alloca(vmlen))) {
1955 memset(passdata, 0, vmlen);
1956 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1957 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
1958 fprintf(p, "%s\n", passdata);
1959 } else
1960 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1961 ast_channel_free(ast);
1962 } else
1963 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1964 } else {
1965 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
1967 "in mailbox %s from %s, on %s so you might\n"
1968 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
1969 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1971 if (attach_user_voicemail) {
1972 /* Eww. We want formats to tell us their own MIME type */
1973 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
1974 char tmpdir[256], newtmp[256];
1975 int tmpfd;
1977 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
1978 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
1979 tmpfd = mkstemp(newtmp);
1980 ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
1981 if (vmu->volgain < -.001 || vmu->volgain > .001) {
1982 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
1983 ast_safe_system(tmpcmd);
1984 attach = newtmp;
1985 ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
1987 fprintf(p, "--%s\n", bound);
1988 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\n", ctype, format, msgnum, format);
1989 fprintf(p, "Content-Transfer-Encoding: base64\n");
1990 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
1991 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
1993 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1994 base_encode(fname, p);
1995 #ifdef IMAP_STORAGE
1996 /* only attach if necessary */
1997 if (strcmp(format, "gsm")) {
1998 fprintf(p, "--%s\n", bound);
1999 fprintf(p, "Content-Type: audio/x-gsm; name=\"msg%04d.%s\"\n", msgnum, format);
2000 fprintf(p, "Content-Transfer-Encoding: base64\n");
2001 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
2002 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.gsm\"\n\n", msgnum);
2003 snprintf(fname, sizeof(fname), "%s.gsm", attach);
2004 base_encode(fname, p);
2006 #endif
2007 fprintf(p, "\n\n--%s--\n.\n", bound);
2008 if (tmpfd > -1)
2009 close(tmpfd);
2010 unlink(newtmp);
2012 fclose(p);
2013 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2014 ast_safe_system(tmp2);
2015 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2017 return 0;
2020 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)
2022 char date[256];
2023 char host[MAXHOSTNAMELEN]="";
2024 char who[256];
2025 char dur[256];
2026 char tmp[80] = "/tmp/astmail-XXXXXX";
2027 char tmp2[256];
2028 struct tm tm;
2029 FILE *p;
2031 if ((p = vm_mkftemp(tmp)) == NULL) {
2032 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2033 return -1;
2034 } else {
2035 gethostname(host, sizeof(host)-1);
2036 if (strchr(srcemail, '@'))
2037 ast_copy_string(who, srcemail, sizeof(who));
2038 else {
2039 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2041 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2042 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2043 fprintf(p, "Date: %s\n", date);
2045 if (*pagerfromstring) {
2046 struct ast_channel *ast;
2047 if ((ast = ast_channel_alloc(0))) {
2048 char *passdata;
2049 int vmlen = strlen(fromstring)*3 + 200;
2050 if ((passdata = alloca(vmlen))) {
2051 memset(passdata, 0, vmlen);
2052 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2053 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2054 fprintf(p, "From: %s <%s>\n", passdata, who);
2055 } else
2056 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2057 ast_channel_free(ast);
2058 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2059 } else
2060 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2061 fprintf(p, "To: %s\n", pager);
2062 if (pagersubject) {
2063 struct ast_channel *ast;
2064 if ((ast = ast_channel_alloc(0))) {
2065 char *passdata;
2066 int vmlen = strlen(pagersubject) * 3 + 200;
2067 if ((passdata = alloca(vmlen))) {
2068 memset(passdata, 0, vmlen);
2069 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2070 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2071 fprintf(p, "Subject: %s\n\n", passdata);
2072 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2073 ast_channel_free(ast);
2074 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2075 } else
2076 fprintf(p, "Subject: New VM\n\n");
2077 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2078 if (pagerbody) {
2079 struct ast_channel *ast;
2080 if ((ast = ast_channel_alloc(0))) {
2081 char *passdata;
2082 int vmlen = strlen(pagerbody)*3 + 200;
2083 if ((passdata = alloca(vmlen))) {
2084 memset(passdata, 0, vmlen);
2085 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2086 pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2087 fprintf(p, "%s\n", passdata);
2088 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2089 ast_channel_free(ast);
2090 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2091 } else {
2092 fprintf(p, "New %s long msg in box %s\n"
2093 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2095 fclose(p);
2096 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2097 ast_safe_system(tmp2);
2098 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
2100 return 0;
2103 #ifndef IMAP_STORAGE
2104 static int get_date(char *s, int len)
2106 struct tm tm;
2107 time_t t;
2108 t = time(0);
2109 localtime_r(&t,&tm);
2110 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2112 #endif
2114 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2116 int res;
2117 char fn[256];
2118 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2119 RETRIEVE(fn, -1);
2120 if (ast_fileexists(fn, NULL, NULL) > 0) {
2121 res = ast_stream_and_wait(chan, fn, chan->language, ecodes);
2122 if (res) {
2123 DISPOSE(fn, -1);
2124 return res;
2126 } else {
2127 /* Dispose just in case */
2128 DISPOSE(fn, -1);
2129 res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
2130 if (res)
2131 return res;
2132 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2133 if (res)
2134 return res;
2136 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
2137 return res;
2140 static void free_user(struct ast_vm_user *vmu)
2142 if (ast_test_flag(vmu, VM_ALLOCED))
2143 free(vmu);
2146 static void free_zone(struct vm_zone *z)
2148 free(z);
2151 static const char *mbox(int id)
2153 static const char *msgs[] = {
2154 "INBOX",
2155 "Old",
2156 "Work",
2157 "Family",
2158 "Friends",
2159 "Cust1",
2160 "Cust2",
2161 "Cust3",
2162 "Cust4",
2163 "Cust5",
2165 return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2168 #ifdef ODBC_STORAGE
2169 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2171 int x = -1;
2172 int res;
2173 SQLHSTMT stmt;
2174 char sql[256];
2175 char rowdata[20];
2176 char tmp[256]="";
2177 struct odbc_obj *obj;
2178 char *context;
2180 if (newmsgs)
2181 *newmsgs = 0;
2182 if (oldmsgs)
2183 *oldmsgs = 0;
2185 /* If no mailbox, return immediately */
2186 if (ast_strlen_zero(mailbox))
2187 return 0;
2189 ast_copy_string(tmp, mailbox, sizeof(tmp));
2191 context = strchr(tmp, '@');
2192 if (context) {
2193 *context = '\0';
2194 context++;
2195 } else
2196 context = "default";
2198 obj = odbc_request_obj(odbc_database, 0);
2199 if (obj) {
2200 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2201 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2202 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2203 odbc_release_obj(obj);
2204 goto yuck;
2206 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2207 res = SQLPrepare(stmt, sql, SQL_NTS);
2208 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2209 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2210 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2211 odbc_release_obj(obj);
2212 goto yuck;
2214 res = odbc_smart_execute(obj, stmt);
2215 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2216 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2217 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2218 odbc_release_obj(obj);
2219 goto yuck;
2221 res = SQLFetch(stmt);
2222 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2223 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2224 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2225 odbc_release_obj(obj);
2226 goto yuck;
2228 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2229 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2230 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2231 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2232 odbc_release_obj(obj);
2233 goto yuck;
2235 *newmsgs = atoi(rowdata);
2236 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2238 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2239 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2240 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2241 odbc_release_obj(obj);
2242 goto yuck;
2244 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2245 res = SQLPrepare(stmt, sql, SQL_NTS);
2246 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2247 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2248 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2249 odbc_release_obj(obj);
2250 goto yuck;
2252 res = odbc_smart_execute(obj, stmt);
2253 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2254 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2255 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2256 odbc_release_obj(obj);
2257 goto yuck;
2259 res = SQLFetch(stmt);
2260 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2261 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2262 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2263 odbc_release_obj(obj);
2264 goto yuck;
2266 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2267 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2268 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2269 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2270 odbc_release_obj(obj);
2271 goto yuck;
2273 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2274 odbc_release_obj(obj);
2275 *oldmsgs = atoi(rowdata);
2276 x = 0;
2277 } else
2278 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2280 yuck:
2281 return x;
2284 static int messagecount(const char *context, const char *mailbox, const char *folder)
2286 struct odbc_obj *obj = NULL;
2287 int nummsgs = 0;
2288 int res;
2289 SQLHSTMT stmt = NULL;
2290 char sql[256];
2291 char rowdata[20];
2292 if (!folder)
2293 folder = "INBOX";
2294 /* If no mailbox, return immediately */
2295 if (ast_strlen_zero(mailbox))
2296 return 0;
2298 obj = odbc_request_obj(odbc_database, 0);
2299 if (obj) {
2300 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2301 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2302 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2303 goto yuck;
2305 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2306 res = SQLPrepare(stmt, sql, SQL_NTS);
2307 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2308 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2309 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2310 goto yuck;
2312 res = odbc_smart_execute(obj, stmt);
2313 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2314 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2315 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2316 goto yuck;
2318 res = SQLFetch(stmt);
2319 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2320 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2321 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2322 goto yuck;
2324 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2325 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2326 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2327 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2328 goto yuck;
2330 nummsgs = atoi(rowdata);
2331 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2332 } else
2333 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2335 yuck:
2336 if (obj)
2337 odbc_release_obj(obj);
2338 return nummsgs;
2341 static int has_voicemail(const char *mailbox, const char *folder)
2343 char *context, tmp[256];
2344 ast_copy_string(tmp, mailbox, sizeof(tmp));
2345 if ((context = strchr(tmp, '@')))
2346 *context++ = '\0';
2347 else
2348 context = "default";
2350 if (messagecount(context, tmp, folder))
2351 return 1;
2352 else
2353 return 0;
2356 #else
2357 #ifdef IMAP_STORAGE
2358 static int count_messages_imap(const char *mailbox, int *newmsgs, int *oldmsgs)
2360 SEARCHPGM *pgm;
2361 SEARCHHEADER *hdr;
2363 struct ast_vm_user *vmu;
2364 struct vm_state *vms_p;
2365 char tmp[256]="";
2366 char *mb, *cur;
2367 char *mailboxnc;
2368 char *context;
2369 int ret = 0;
2370 if (newmsgs)
2371 *newmsgs = 0;
2372 if (oldmsgs)
2373 *oldmsgs = 0;
2375 if(option_debug > 2)
2376 ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox);
2377 /* If no mailbox, return immediately */
2378 if (ast_strlen_zero(mailbox))
2379 return 0;
2380 if (strchr(mailbox, ',')) {
2381 int tmpnew, tmpold;
2382 ast_copy_string(tmp, mailbox, sizeof(tmp));
2383 mb = tmp;
2384 ret = 0;
2385 while((cur = strsep(&mb, ", "))) {
2386 if (!ast_strlen_zero(cur)) {
2387 if (count_messages_imap(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2388 return -1;
2389 else {
2390 if (newmsgs)
2391 *newmsgs += tmpnew;
2392 if (oldmsgs)
2393 *oldmsgs += tmpold;
2397 return 0;
2399 ast_copy_string(tmp, mailbox, sizeof(tmp));
2400 context = strchr(tmp, '@');
2401 if (context) {
2402 *context = '\0';
2403 mailboxnc = tmp;
2404 context++;
2405 } else {
2406 context = "default";
2407 mailboxnc = (char *)mailbox;
2410 /* We have to get the user before we can open the stream! */
2411 /*ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2412 vmu = find_user(NULL, context, mailboxnc);
2413 if (!vmu) {
2414 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailboxnc,context);
2415 return -1;
2416 } else {
2417 /* No IMAP account available */
2418 if (vmu->imapuser[0] == '\0') {
2419 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2420 return -1;
2424 /* check if someone is accessing this box right now... */
2425 vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
2426 if (!vms_p) {
2427 vms_p = get_vm_state_by_mailbox(mailboxnc,1);
2429 if (vms_p) {
2430 if(option_debug > 2)
2431 ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
2432 *newmsgs = vms_p->newmessages;
2433 *oldmsgs = vms_p->oldmessages;
2434 return 0;
2437 /* add one if not there... */
2438 vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
2439 if (!vms_p) {
2440 vms_p = get_vm_state_by_mailbox(mailboxnc,0);
2443 if (!vms_p) {
2444 if(option_debug > 2)
2445 ast_log (LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2446 vms_p = (struct vm_state *)malloc(sizeof(struct vm_state));
2447 strcpy(vms_p->imapuser,vmu->imapuser);
2448 ast_copy_string(vms_p->username, mailboxnc, sizeof(vms_p->username)); /* save for access from interactive entry point */
2449 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2450 if(option_debug > 2)
2451 ast_log (LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2452 vms_p->updated = 1;
2453 vms_p->interactive = 0;
2454 /* set mailbox to INBOX! */
2455 ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
2456 init_vm_state(vms_p);
2457 vmstate_insert(vms_p);
2459 if (!vms_p->mailstream)
2460 ret = init_mailstream(vms_p);
2461 if (!vms_p->mailstream) {
2462 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2463 return -1;
2465 if (newmsgs && ret==0 && vms_p->updated==1 ) {
2466 pgm = mail_newsearchpgm ();
2467 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2468 pgm->header = hdr;
2469 pgm->unseen = 1;
2470 pgm->seen = 0;
2471 pgm->undeleted = 1;
2472 pgm->deleted = 0;
2474 vms_p->vmArrayIndex = 0;
2476 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2477 *newmsgs = vms_p->vmArrayIndex;
2478 vms_p->newmessages = vms_p->vmArrayIndex;
2480 if (oldmsgs && ret==0 && vms_p->updated==1 ) {
2481 pgm = mail_newsearchpgm ();
2482 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2483 pgm->header = hdr;
2484 pgm->unseen = 0;
2485 pgm->seen = 1;
2486 pgm->deleted = 0;
2487 pgm->undeleted = 1;
2489 vms_p->vmArrayIndex = 0;
2491 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2492 *oldmsgs = vms_p->vmArrayIndex;
2493 vms_p->oldmessages = vms_p->vmArrayIndex;
2495 if (vms_p->updated == 1) { /* changes, so we did the searches above */
2496 vms_p->updated = 0;
2497 } else if (vms_p->updated > 1) { /* decrement delay count */
2498 vms_p->updated--;
2499 } else { /* no changes, so don't search */
2500 mail_ping(vms_p->mailstream);
2501 /* Keep the old data */
2502 *newmsgs = vms_p->newmessages;
2503 *oldmsgs = vms_p->oldmessages;
2505 return 0;
2507 #endif
2508 #endif
2510 /* copy message only used by file storage */
2511 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)
2514 char fromdir[256], todir[256], frompath[256], topath[256];
2515 const char *frombox = mbox(imbox);
2516 int recipmsgnum;
2518 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2520 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2522 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2523 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2525 if (vm_lock_path(todir))
2526 return ERROR_LOCK_PATH;
2528 recipmsgnum = 0;
2529 do {
2530 make_file(topath, sizeof(topath), todir, recipmsgnum);
2531 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
2532 break;
2533 recipmsgnum++;
2534 } while (recipmsgnum < recip->maxmsg);
2535 if (recipmsgnum < recip->maxmsg) {
2536 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2537 } else {
2538 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2540 ast_unlock_path(todir);
2541 notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2543 return 0;
2546 #ifndef ODBC_STORAGE
2548 static int messagecount(const char *context, const char *mailbox, const char *folder)
2550 return __has_voicemail(context, mailbox, folder, 0);
2554 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2556 DIR *dir;
2557 struct dirent *de;
2558 char fn[256];
2559 int ret = 0;
2560 if (!folder)
2561 folder = "INBOX";
2562 /* If no mailbox, return immediately */
2563 if (ast_strlen_zero(mailbox))
2564 return 0;
2565 if (!context)
2566 context = "default";
2567 snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2568 dir = opendir(fn);
2569 if (!dir)
2570 return 0;
2571 while ((de = readdir(dir))) {
2572 if (!strncasecmp(de->d_name, "msg", 3)) {
2573 if (shortcircuit) {
2574 ret = 1;
2575 break;
2576 } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2577 ret++;
2580 closedir(dir);
2581 return ret;
2585 static int has_voicemail(const char *mailbox, const char *folder)
2587 char tmp[256], *tmp2 = tmp, *mbox, *context;
2588 ast_copy_string(tmp, mailbox, sizeof(tmp));
2589 while ((mbox = strsep(&tmp2, ","))) {
2590 if ((context = strchr(mbox, '@')))
2591 *context++ = '\0';
2592 else
2593 context = "default";
2594 if (__has_voicemail(context, mbox, folder, 1))
2595 return 1;
2597 return 0;
2601 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2603 char tmp[256];
2604 char *context;
2606 if (newmsgs)
2607 *newmsgs = 0;
2608 if (oldmsgs)
2609 *oldmsgs = 0;
2610 /* If no mailbox, return immediately */
2611 if (ast_strlen_zero(mailbox))
2612 return 0;
2613 if (strchr(mailbox, ',')) {
2614 int tmpnew, tmpold;
2615 char *mb, *cur;
2617 ast_copy_string(tmp, mailbox, sizeof(tmp));
2618 mb = tmp;
2619 while ((cur = strsep(&mb, ", "))) {
2620 if (!ast_strlen_zero(cur)) {
2621 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2622 return -1;
2623 else {
2624 if (newmsgs)
2625 *newmsgs += tmpnew;
2626 if (oldmsgs)
2627 *oldmsgs += tmpold;
2631 return 0;
2633 ast_copy_string(tmp, mailbox, sizeof(tmp));
2634 context = strchr(tmp, '@');
2635 if (context) {
2636 *context = '\0';
2637 context++;
2638 } else
2639 context = "default";
2640 if (newmsgs)
2641 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2642 if (oldmsgs)
2643 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2644 return 0;
2647 #endif
2649 static void run_externnotify(char *context, char *extension)
2651 char arguments[255];
2652 char ext_context[256] = "";
2653 int newvoicemails = 0, oldvoicemails = 0;
2654 struct ast_smdi_mwi_message *mwi_msg;
2656 if (!ast_strlen_zero(context))
2657 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2658 else
2659 ast_copy_string(ext_context, extension, sizeof(ext_context));
2661 if (!strcasecmp(externnotify, "smdi")) {
2662 if (ast_app_has_voicemail(ext_context, NULL))
2663 ast_smdi_mwi_set(smdi_iface, extension);
2664 else
2665 ast_smdi_mwi_unset(smdi_iface, extension);
2667 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2668 ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2669 if (!strncmp(mwi_msg->cause, "INV", 3))
2670 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2671 else if (!strncmp(mwi_msg->cause, "BLK", 3))
2672 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2673 ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2674 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2675 } else {
2676 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2678 } else if (!ast_strlen_zero(externnotify)) {
2679 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2680 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2681 } else {
2682 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2683 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2684 ast_safe_system(arguments);
2689 struct leave_vm_options {
2690 unsigned int flags;
2691 signed char record_gain;
2694 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2696 #ifdef IMAP_STORAGE
2697 int newmsgs, oldmsgs;
2698 struct vm_state *vms;
2699 #else
2700 char tmptxtfile[256], txtfile[256];
2701 char callerid[256];
2702 FILE *txt;
2703 char date[256];
2704 int txtdes;
2705 #endif
2706 int res = 0;
2707 int msgnum;
2708 int duration = 0;
2709 int ausemacro = 0;
2710 int ousemacro = 0;
2711 int ouseexten = 0;
2712 char dir[256], tmpdir[260];
2713 char fn[256];
2714 char prefile[256]="";
2715 char tempfile[256]="";
2716 char ext_context[256] = "";
2717 char fmt[80];
2718 char *context;
2719 char ecodes[16] = "#";
2720 char tmp[256] = "", *tmpptr;
2721 struct ast_vm_user *vmu;
2722 struct ast_vm_user svm;
2723 const char *category = NULL;
2725 ast_copy_string(tmp, ext, sizeof(tmp));
2726 ext = tmp;
2727 context = strchr(tmp, '@');
2728 if (context) {
2729 *context++ = '\0';
2730 tmpptr = strchr(context, '&');
2731 } else {
2732 tmpptr = strchr(ext, '&');
2735 if (tmpptr)
2736 *tmpptr++ = '\0';
2738 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2740 if(option_debug > 2)
2741 ast_log(LOG_DEBUG, "Before find_user\n");
2742 if (!(vmu = find_user(&svm, context, ext))) {
2743 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2744 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2745 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2746 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2747 return res;
2750 /* Setup pre-file if appropriate */
2751 if (strcmp(vmu->context, "default"))
2752 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2753 else
2754 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2755 if (ast_test_flag(options, OPT_BUSY_GREETING))
2756 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2757 else if (ast_test_flag(options, OPT_UNAVAIL_GREETING))
2758 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2759 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2760 RETRIEVE(tempfile, -1);
2761 if (ast_fileexists(tempfile, NULL, NULL) > 0)
2762 ast_copy_string(prefile, tempfile, sizeof(prefile));
2763 DISPOSE(tempfile, -1);
2764 /* It's easier just to try to make it than to check for its existence */
2765 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2766 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
2768 /* Check current or macro-calling context for special extensions */
2769 if (ast_test_flag(vmu, VM_OPERATOR)) {
2770 if (!ast_strlen_zero(vmu->exit)) {
2771 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
2772 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2773 ouseexten = 1;
2775 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
2776 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2777 ouseexten = 1;
2779 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2780 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2781 ousemacro = 1;
2785 if (!ast_strlen_zero(vmu->exit)) {
2786 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2787 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2788 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2789 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2790 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2791 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2792 ausemacro = 1;
2795 /* Play the beginning intro if desired */
2796 if (!ast_strlen_zero(prefile)) {
2797 RETRIEVE(prefile, -1);
2798 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2799 if (ast_streamfile(chan, prefile, chan->language) > -1)
2800 res = ast_waitstream(chan, ecodes);
2801 } else {
2802 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2803 res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2805 DISPOSE(prefile, -1);
2806 if (res < 0) {
2807 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2808 free_user(vmu);
2809 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2810 return -1;
2813 if (res == '#') {
2814 /* On a '#' we skip the instructions */
2815 ast_set_flag(options, OPT_SILENT);
2816 res = 0;
2818 if (!res && !ast_test_flag(options, OPT_SILENT)) {
2819 res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
2820 if (res == '#') {
2821 ast_set_flag(options, OPT_SILENT);
2822 res = 0;
2825 if (res > 0)
2826 ast_stopstream(chan);
2827 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2828 other than the operator -- an automated attendant or mailbox login for example */
2829 if (res == '*') {
2830 chan->exten[0] = 'a';
2831 chan->exten[1] = '\0';
2832 if (!ast_strlen_zero(vmu->exit)) {
2833 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2834 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2835 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2837 chan->priority = 0;
2838 free_user(vmu);
2839 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2840 return 0;
2843 /* Check for a '0' here */
2844 if (res == '0') {
2845 transfer:
2846 if (ouseexten || ousemacro) {
2847 chan->exten[0] = 'o';
2848 chan->exten[1] = '\0';
2849 if (!ast_strlen_zero(vmu->exit)) {
2850 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2851 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2852 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2854 ast_play_and_wait(chan, "transfer");
2855 chan->priority = 0;
2856 free_user(vmu);
2857 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2859 return 0;
2861 if (res < 0) {
2862 free_user(vmu);
2863 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2864 return -1;
2866 /* The meat of recording the message... All the announcements and beeps have been played*/
2867 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2868 if (!ast_strlen_zero(fmt)) {
2869 msgnum = 0;
2871 #ifdef IMAP_STORAGE
2872 /* Is ext a mailbox? */
2873 /* must open stream for this user to get info! */
2874 vms = get_vm_state_by_mailbox(ext,0);
2875 if (vms) {
2876 if(option_debug > 2)
2877 ast_log(LOG_DEBUG, "Using vm_state, interactive set to %d.\n",vms->interactive);
2878 newmsgs = vms->newmessages++;
2879 oldmsgs = vms->oldmessages;
2880 } else {
2881 res = count_messages_imap(ext, &newmsgs, &oldmsgs);
2882 if(res < 0) {
2883 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
2884 return -1;
2887 /* here is a big difference! We add one to it later */
2888 msgnum = newmsgs + oldmsgs;
2889 ast_log(LOG_NOTICE, "Messagecount set to %d\n",msgnum);
2890 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
2891 /* set variable for compatability */
2892 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
2894 /* Check if mailbox is full */
2895 if (vms->quota_usage >= vms->quota_limit) {
2896 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!!\n");
2897 ast_play_and_wait(chan, "vm-mailboxfull");
2898 return -1;
2901 /* play beep */
2902 res = ast_streamfile(chan, "beep", chan->language);
2903 if (!res)
2904 res = ast_waitstream(chan, "");
2905 /* play_record_review does recording and verify */
2906 ast_log(LOG_DEBUG, "About to record message in file %s\n",fn);
2907 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration, dir, options->record_gain);
2908 if (res == '0') {
2909 goto transfer;
2911 if (res > 0) res = 0;
2913 if (duration < vmminmessage) {
2914 if (option_verbose > 2)
2915 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2916 goto leave_vm_out;
2918 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2919 #else
2920 if (count_messages(vmu, dir) >= vmu->maxmsg) {
2921 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2922 if (!res)
2923 res = ast_waitstream(chan, "");
2924 ast_log(LOG_WARNING, "No more messages possible\n");
2925 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2926 goto leave_vm_out;
2929 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
2930 txtdes = mkstemp(tmptxtfile);
2931 if (txtdes < 0) {
2932 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2933 if (!res)
2934 res = ast_waitstream(chan, "");
2935 ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
2936 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2937 goto leave_vm_out;
2940 /* Now play the beep once we have the message number for our next message. */
2941 if (res >= 0) {
2942 /* Unless we're *really* silent, try to send the beep */
2943 res = ast_stream_and_wait(chan, "beep", chan->language, "");
2946 /* Store information */
2947 txt = fdopen(txtdes, "w+");
2948 if (txt) {
2949 get_date(date, sizeof(date));
2950 fprintf(txt,
2951 ";\n"
2952 "; Message Information file\n"
2953 ";\n"
2954 "[message]\n"
2955 "origmailbox=%s\n"
2956 "context=%s\n"
2957 "macrocontext=%s\n"
2958 "exten=%s\n"
2959 "priority=%d\n"
2960 "callerchan=%s\n"
2961 "callerid=%s\n"
2962 "origdate=%s\n"
2963 "origtime=%ld\n"
2964 "category=%s\n",
2965 ext,
2966 chan->context,
2967 chan->macrocontext,
2968 chan->exten,
2969 chan->priority,
2970 chan->name,
2971 ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
2972 date, (long)time(NULL),
2973 category ? category : "");
2974 } else
2975 ast_log(LOG_WARNING, "Error opening text file for output\n");
2976 res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain);
2978 if (txt) {
2979 if (duration < vmminmessage) {
2980 if (option_verbose > 2)
2981 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2982 ast_filedelete(tmptxtfile, NULL);
2983 unlink(tmptxtfile);
2984 } else {
2985 fprintf(txt, "duration=%d\n", duration);
2986 fclose(txt);
2987 if (vm_lock_path(dir)) {
2988 ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
2989 /* Delete files */
2990 ast_filedelete(tmptxtfile, NULL);
2991 unlink(tmptxtfile);
2992 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
2993 if (option_debug)
2994 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
2995 unlink(tmptxtfile);
2996 ast_unlock_path(dir);
2997 } else {
2998 for (;;) {
2999 make_file(fn, sizeof(fn), dir, msgnum);
3000 if (!EXISTS(dir, msgnum, fn, NULL))
3001 break;
3002 msgnum++;
3005 /* assign a variable with the name of the voicemail file */
3006 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3008 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3009 ast_filerename(tmptxtfile, fn, NULL);
3010 rename(tmptxtfile, txtfile);
3012 ast_unlock_path(dir);
3013 /* Are there to be more recipients of this message? */
3014 while (tmpptr) {
3015 struct ast_vm_user recipu, *recip;
3016 char *exten, *context;
3018 exten = strsep(&tmpptr, "&");
3019 context = strchr(exten, '@');
3020 if (context) {
3021 *context = '\0';
3022 context++;
3024 if ((recip = find_user(&recipu, context, exten))) {
3025 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
3026 free_user(recip);
3029 if (ast_fileexists(fn, NULL, NULL)) {
3030 STORE(dir, vmu->mailbox, vmu->context, msgnum);
3031 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
3032 DISPOSE(dir, msgnum);
3037 #endif
3038 if (res == '0') {
3039 goto transfer;
3040 } else if (res > 0)
3041 res = 0;
3043 if (duration < vmminmessage)
3044 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3045 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3046 else
3047 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3048 } else
3049 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3050 leave_vm_out:
3051 free_user(vmu);
3053 return res;
3056 #ifndef IMAP_STORAGE
3057 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3059 /* we know max messages, so stop process when number is hit */
3061 int x,dest;
3062 char sfn[256];
3063 char dfn[256];
3065 if (vm_lock_path(dir))
3066 return ERROR_LOCK_PATH;
3068 for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3069 make_file(sfn, sizeof(sfn), dir, x);
3070 if (EXISTS(dir, x, sfn, NULL)) {
3072 if (x != dest) {
3073 make_file(dfn, sizeof(dfn), dir, dest);
3074 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3077 dest++;
3080 ast_unlock_path(dir);
3082 return 0;
3084 #endif
3086 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3088 int d;
3089 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
3090 return d;
3093 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3095 #ifdef IMAP_STORAGE
3096 /* we must use mbox(x) folder names, and copy the message there */
3097 /* simple. huh? */
3098 char *dbox = mbox(box);
3099 long res;
3100 char sequence[10];
3102 /* if save to Old folder, just leave in INBOX */
3103 if (box == 1) return 10;
3104 /* get the real IMAP message number for this message */
3105 sprintf(sequence,"%ld",vms->msgArray[msg]);
3106 if(option_debug > 2)
3107 ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,dbox);
3108 res = mail_copy(vms->mailstream,sequence,dbox);
3109 if (res == 1) return 0;
3110 return 1;
3111 #else
3112 char *dir = vms->curdir;
3113 char *username = vms->username;
3114 char *context = vmu->context;
3115 char sfn[256];
3116 char dfn[256];
3117 char ddir[256];
3118 const char *dbox = mbox(box);
3119 int x;
3120 make_file(sfn, sizeof(sfn), dir, msg);
3121 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
3123 if (vm_lock_path(ddir))
3124 return ERROR_LOCK_PATH;
3126 for (x = 0; x < vmu->maxmsg; x++) {
3127 make_file(dfn, sizeof(dfn), ddir, x);
3128 if (!EXISTS(ddir, x, dfn, NULL))
3129 break;
3131 if (x >= vmu->maxmsg) {
3132 ast_unlock_path(ddir);
3133 return -1;
3135 if (strcmp(sfn, dfn)) {
3136 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
3138 ast_unlock_path(ddir);
3139 #endif
3140 return 0;
3143 static int adsi_logo(unsigned char *buf)
3145 int bytes = 0;
3146 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
3147 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
3148 return bytes;
3151 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
3153 unsigned char buf[256];
3154 int bytes=0;
3155 int x;
3156 char num[5];
3158 *useadsi = 0;
3159 bytes += adsi_data_mode(buf + bytes);
3160 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3162 bytes = 0;
3163 bytes += adsi_logo(buf);
3164 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3165 #ifdef DISPLAY
3166 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
3167 #endif
3168 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3169 bytes += adsi_data_mode(buf + bytes);
3170 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3172 if (adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
3173 bytes = 0;
3174 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
3175 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3176 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3177 bytes += adsi_voice_mode(buf + bytes, 0);
3178 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3179 return 0;
3182 #ifdef DISPLAY
3183 /* Add a dot */
3184 bytes = 0;
3185 bytes += adsi_logo(buf);
3186 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3187 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
3188 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3189 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3190 #endif
3191 bytes = 0;
3192 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
3193 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
3194 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
3195 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
3196 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
3197 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
3198 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3200 #ifdef DISPLAY
3201 /* Add another dot */
3202 bytes = 0;
3203 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
3204 bytes += adsi_voice_mode(buf + bytes, 0);
3206 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3207 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3208 #endif
3210 bytes = 0;
3211 /* These buttons we load but don't use yet */
3212 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
3213 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
3214 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
3215 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
3216 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
3217 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
3218 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3220 #ifdef DISPLAY
3221 /* Add another dot */
3222 bytes = 0;
3223 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
3224 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3225 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3226 #endif
3228 bytes = 0;
3229 for (x=0;x<5;x++) {
3230 snprintf(num, sizeof(num), "%d", x);
3231 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
3233 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
3234 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3236 #ifdef DISPLAY
3237 /* Add another dot */
3238 bytes = 0;
3239 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
3240 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3241 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3242 #endif
3244 if (adsi_end_download(chan)) {
3245 bytes = 0;
3246 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
3247 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3248 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3249 bytes += adsi_voice_mode(buf + bytes, 0);
3250 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3251 return 0;
3253 bytes = 0;
3254 bytes += adsi_download_disconnect(buf + bytes);
3255 bytes += adsi_voice_mode(buf + bytes, 0);
3256 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3258 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
3260 #ifdef DISPLAY
3261 /* Add last dot */
3262 bytes = 0;
3263 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
3264 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3265 #endif
3266 ast_log(LOG_DEBUG, "Restarting session...\n");
3268 bytes = 0;
3269 /* Load the session now */
3270 if (adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
3271 *useadsi = 1;
3272 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
3273 } else
3274 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
3276 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3277 return 0;
3280 static void adsi_begin(struct ast_channel *chan, int *useadsi)
3282 int x;
3283 if (!adsi_available(chan))
3284 return;
3285 x = adsi_load_session(chan, adsifdn, adsiver, 1);
3286 if (x < 0)
3287 return;
3288 if (!x) {
3289 if (adsi_load_vmail(chan, useadsi)) {
3290 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
3291 return;
3293 } else
3294 *useadsi = 1;
3297 static void adsi_login(struct ast_channel *chan)
3299 unsigned char buf[256];
3300 int bytes=0;
3301 unsigned char keys[8];
3302 int x;
3303 if (!adsi_available(chan))
3304 return;
3306 for (x=0;x<8;x++)
3307 keys[x] = 0;
3308 /* Set one key for next */
3309 keys[3] = ADSI_KEY_APPS + 3;
3311 bytes += adsi_logo(buf + bytes);
3312 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
3313 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
3314 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3315 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
3316 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
3317 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
3318 bytes += adsi_set_keys(buf + bytes, keys);
3319 bytes += adsi_voice_mode(buf + bytes, 0);
3320 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3323 static void adsi_password(struct ast_channel *chan)
3325 unsigned char buf[256];
3326 int bytes=0;
3327 unsigned char keys[8];
3328 int x;
3329 if (!adsi_available(chan))
3330 return;
3332 for (x=0;x<8;x++)
3333 keys[x] = 0;
3334 /* Set one key for next */
3335 keys[3] = ADSI_KEY_APPS + 3;
3337 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3338 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
3339 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
3340 bytes += adsi_set_keys(buf + bytes, keys);
3341 bytes += adsi_voice_mode(buf + bytes, 0);
3342 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3345 static void adsi_folders(struct ast_channel *chan, int start, char *label)
3347 unsigned char buf[256];
3348 int bytes=0;
3349 unsigned char keys[8];
3350 int x,y;
3352 if (!adsi_available(chan))
3353 return;
3355 for (x=0;x<5;x++) {
3356 y = ADSI_KEY_APPS + 12 + start + x;
3357 if (y > ADSI_KEY_APPS + 12 + 4)
3358 y = 0;
3359 keys[x] = ADSI_KEY_SKT | y;
3361 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
3362 keys[6] = 0;
3363 keys[7] = 0;
3365 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
3366 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
3367 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3368 bytes += adsi_set_keys(buf + bytes, keys);
3369 bytes += adsi_voice_mode(buf + bytes, 0);
3371 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3374 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
3376 int bytes=0;
3377 unsigned char buf[256];
3378 char buf1[256], buf2[256];
3379 char fn2[256];
3381 char cid[256]="";
3382 char *val;
3383 char *name, *num;
3384 char datetime[21]="";
3385 FILE *f;
3387 unsigned char keys[8];
3389 int x;
3391 if (!adsi_available(chan))
3392 return;
3394 /* Retrieve important info */
3395 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
3396 f = fopen(fn2, "r");
3397 if (f) {
3398 while (!feof(f)) {
3399 fgets((char *)buf, sizeof(buf), f);
3400 if (!feof(f)) {
3401 char *stringp=NULL;
3402 stringp = (char *)buf;
3403 strsep(&stringp, "=");
3404 val = strsep(&stringp, "=");
3405 if (!ast_strlen_zero(val)) {
3406 if (!strcmp((char *)buf, "callerid"))
3407 ast_copy_string(cid, val, sizeof(cid));
3408 if (!strcmp((char *)buf, "origdate"))
3409 ast_copy_string(datetime, val, sizeof(datetime));
3413 fclose(f);
3415 /* New meaning for keys */
3416 for (x=0;x<5;x++)
3417 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3418 keys[6] = 0x0;
3419 keys[7] = 0x0;
3421 if (!vms->curmsg) {
3422 /* No prev key, provide "Folder" instead */
3423 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3425 if (vms->curmsg >= vms->lastmsg) {
3426 /* If last message ... */
3427 if (vms->curmsg) {
3428 /* but not only message, provide "Folder" instead */
3429 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3430 bytes += adsi_voice_mode(buf + bytes, 0);
3432 } else {
3433 /* Otherwise if only message, leave blank */
3434 keys[3] = 1;
3438 if (!ast_strlen_zero(cid)) {
3439 ast_callerid_parse(cid, &name, &num);
3440 if (!name)
3441 name = num;
3442 } else
3443 name = "Unknown Caller";
3445 /* If deleted, show "undeleted" */
3447 if (vms->deleted[vms->curmsg])
3448 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3450 /* Except "Exit" */
3451 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3452 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
3453 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
3454 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
3456 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3457 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3458 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
3459 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
3460 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3461 bytes += adsi_set_keys(buf + bytes, keys);
3462 bytes += adsi_voice_mode(buf + bytes, 0);
3464 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3467 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
3469 int bytes=0;
3470 unsigned char buf[256];
3471 unsigned char keys[8];
3473 int x;
3475 if (!adsi_available(chan))
3476 return;
3478 /* New meaning for keys */
3479 for (x=0;x<5;x++)
3480 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3482 keys[6] = 0x0;
3483 keys[7] = 0x0;
3485 if (!vms->curmsg) {
3486 /* No prev key, provide "Folder" instead */
3487 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3489 if (vms->curmsg >= vms->lastmsg) {
3490 /* If last message ... */
3491 if (vms->curmsg) {
3492 /* but not only message, provide "Folder" instead */
3493 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3494 } else {
3495 /* Otherwise if only message, leave blank */
3496 keys[3] = 1;
3500 /* If deleted, show "undeleted" */
3501 if (vms->deleted[vms->curmsg])
3502 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3504 /* Except "Exit" */
3505 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3506 bytes += adsi_set_keys(buf + bytes, keys);
3507 bytes += adsi_voice_mode(buf + bytes, 0);
3509 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3512 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
3514 unsigned char buf[256] = "";
3515 char buf1[256] = "", buf2[256] = "";
3516 int bytes=0;
3517 unsigned char keys[8];
3518 int x;
3520 char *newm = (vms->newmessages == 1) ? "message" : "messages";
3521 char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
3522 if (!adsi_available(chan))
3523 return;
3524 if (vms->newmessages) {
3525 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
3526 if (vms->oldmessages) {
3527 strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
3528 snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
3529 } else {
3530 snprintf(buf2, sizeof(buf2), "%s.", newm);
3532 } else if (vms->oldmessages) {
3533 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
3534 snprintf(buf2, sizeof(buf2), "%s.", oldm);
3535 } else {
3536 strcpy(buf1, "You have no messages.");
3537 buf2[0] = ' ';
3538 buf2[1] = '\0';
3540 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3541 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3542 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3544 for (x=0;x<6;x++)
3545 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3546 keys[6] = 0;
3547 keys[7] = 0;
3549 /* Don't let them listen if there are none */
3550 if (vms->lastmsg < 0)
3551 keys[0] = 1;
3552 bytes += adsi_set_keys(buf + bytes, keys);
3554 bytes += adsi_voice_mode(buf + bytes, 0);
3556 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3559 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
3561 unsigned char buf[256] = "";
3562 char buf1[256] = "", buf2[256] = "";
3563 int bytes=0;
3564 unsigned char keys[8];
3565 int x;
3567 char *mess = (vms->lastmsg == 0) ? "message" : "messages";
3569 if (!adsi_available(chan))
3570 return;
3572 /* Original command keys */
3573 for (x=0;x<6;x++)
3574 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3576 keys[6] = 0;
3577 keys[7] = 0;
3579 if ((vms->lastmsg + 1) < 1)
3580 keys[0] = 0;
3582 snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
3583 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
3585 if (vms->lastmsg + 1)
3586 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
3587 else
3588 strcpy(buf2, "no messages.");
3589 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3590 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3591 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
3592 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3593 bytes += adsi_set_keys(buf + bytes, keys);
3595 bytes += adsi_voice_mode(buf + bytes, 0);
3597 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3602 static void adsi_clear(struct ast_channel *chan)
3604 char buf[256];
3605 int bytes=0;
3606 if (!adsi_available(chan))
3607 return;
3608 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3609 bytes += adsi_voice_mode(buf + bytes, 0);
3611 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3615 static void adsi_goodbye(struct ast_channel *chan)
3617 unsigned char buf[256];
3618 int bytes=0;
3620 if (!adsi_available(chan))
3621 return;
3622 bytes += adsi_logo(buf + bytes);
3623 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
3624 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
3625 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3626 bytes += adsi_voice_mode(buf + bytes, 0);
3628 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3631 /*--- get_folder: Folder menu ---*/
3632 /* Plays "press 1 for INBOX messages" etc
3633 Should possibly be internationalized
3635 static int get_folder(struct ast_channel *chan, int start)
3637 int x;
3638 int d;
3639 char fn[256];
3640 d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
3641 if (d)
3642 return d;
3643 for (x = start; x< 5; x++) { /* For all folders */
3644 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
3645 return d;
3646 d = ast_play_and_wait(chan, "vm-for"); /* "for" */
3647 if (d)
3648 return d;
3649 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
3650 d = vm_play_folder_name(chan, fn);
3651 if (d)
3652 return d;
3653 d = ast_waitfordigit(chan, 500);
3654 if (d)
3655 return d;
3657 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
3658 if (d)
3659 return d;
3660 d = ast_waitfordigit(chan, 4000);
3661 return d;
3664 static int get_folder2(struct ast_channel *chan, char *fn, int start)
3666 int res = 0;
3667 res = ast_play_and_wait(chan, fn); /* Folder name */
3668 while (((res < '0') || (res > '9')) &&
3669 (res != '#') && (res >= 0)) {
3670 res = get_folder(chan, 0);
3672 return res;
3675 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context, signed char record_gain)
3677 int cmd = 0;
3678 int retries = 0;
3679 int duration = 0;
3680 signed char zero_gain = 0;
3682 while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
3683 if (cmd)
3684 retries = 0;
3685 switch (cmd) {
3686 case '1':
3687 /* prepend a message to the current message and return */
3689 char file[200];
3690 snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
3691 if (record_gain)
3692 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
3693 cmd = ast_play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1, silencethreshold, maxsilence);
3694 if (record_gain)
3695 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
3696 break;
3698 case '2':
3699 cmd = 't';
3700 break;
3701 case '*':
3702 cmd = '*';
3703 break;
3704 default:
3705 cmd = ast_play_and_wait(chan,"vm-forwardoptions");
3706 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
3707 if (!cmd)
3708 cmd = ast_play_and_wait(chan,"vm-starmain");
3709 /* "press star to return to the main menu" */
3710 if (!cmd)
3711 cmd = ast_waitfordigit(chan,6000);
3712 if (!cmd)
3713 retries++;
3714 if (retries > 3)
3715 cmd = 't';
3718 if (cmd == 't' || cmd == 'S')
3719 cmd = 0;
3720 return cmd;
3723 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
3725 char todir[256], fn[256], ext_context[256], *stringp;
3726 int newmsgs = 0, oldmsgs = 0;
3727 const char *category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3729 #ifdef IMAP_STORAGE
3730 snprintf(todir, sizeof(todir), "%s/imap",VM_SPOOL_DIR);
3731 sprintf(fn, "%s/msg%s%04d", todir, vmu->mailbox, msgnum);
3732 #else
3733 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
3734 make_file(fn, sizeof(fn), todir, msgnum);
3735 #endif
3736 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
3738 if (!ast_strlen_zero(vmu->attachfmt)) {
3739 if (strstr(fmt, vmu->attachfmt)) {
3740 fmt = vmu->attachfmt;
3741 } else {
3742 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);
3746 /* Attach only the first format */
3747 fmt = ast_strdupa(fmt);
3748 stringp = fmt;
3749 strsep(&stringp, "|");
3751 if (!ast_strlen_zero(vmu->email)) {
3752 int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
3753 char *myserveremail = serveremail;
3754 attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
3755 if (!ast_strlen_zero(vmu->serveremail))
3756 myserveremail = vmu->serveremail;
3757 /*XXX possible imap issue, should category be NULL XXX*/
3758 sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail, chan, category);
3761 if (!ast_strlen_zero(vmu->pager)) {
3762 char *myserveremail = serveremail;
3763 if (!ast_strlen_zero(vmu->serveremail))
3764 myserveremail = vmu->serveremail;
3765 sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, duration, vmu, category);
3768 if (ast_test_flag(vmu, VM_DELETE)) {
3769 DELETE(todir, msgnum, fn);
3772 /* Leave voicemail for someone */
3773 if (ast_app_has_voicemail(ext_context, NULL)) {
3774 ast_app_inboxcount(ext_context, &newmsgs, &oldmsgs);
3776 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);
3777 run_externnotify(vmu->context, vmu->mailbox);
3778 return 0;
3781 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)
3783 #ifdef IMAP_STORAGE
3784 BODY *body;
3785 char *header_content;
3786 char *temp;
3787 #else
3788 struct ast_config *mif;
3789 char sys[256];
3790 char miffile[256];
3791 char callerid[512];
3792 char tmp[256];
3793 char ext_context[256]="";
3794 #endif
3795 char username[70]="";
3796 char todir[256];
3797 int todircount=0;
3798 int duration;
3799 char fn[256];
3800 int res = 0, cmd = 0;
3801 struct ast_vm_user *receiver = NULL, *vmtmp;
3802 AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
3803 char *stringp, *s;
3804 int saved_messages = 0, found = 0;
3805 int valid_extensions = 0;
3806 char *dir;
3807 int curmsg;
3809 if (vms == NULL) return -1;
3810 dir = vms->curdir;
3811 curmsg = vms->curmsg;
3813 while (!res && !valid_extensions) {
3814 int use_directory = 0;
3815 if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
3816 int done = 0;
3817 int retries = 0;
3818 cmd=0;
3819 while ((cmd >= 0) && !done ){
3820 if (cmd)
3821 retries = 0;
3822 switch (cmd) {
3823 case '1':
3824 use_directory = 0;
3825 done = 1;
3826 break;
3827 case '2':
3828 use_directory = 1;
3829 done=1;
3830 break;
3831 case '*':
3832 cmd = 't';
3833 done = 1;
3834 break;
3835 default:
3836 /* Press 1 to enter an extension press 2 to use the directory */
3837 cmd = ast_play_and_wait(chan,"vm-forward");
3838 if (!cmd)
3839 cmd = ast_waitfordigit(chan,3000);
3840 if (!cmd)
3841 retries++;
3842 if (retries > 3)
3844 cmd = 't';
3845 done = 1;
3850 if (cmd < 0 || cmd == 't')
3851 break;
3854 if (use_directory) {
3855 /* use app_directory */
3857 char old_context[sizeof(chan->context)];
3858 char old_exten[sizeof(chan->exten)];
3859 int old_priority;
3860 struct ast_app* app;
3863 app = pbx_findapp("Directory");
3864 if (app) {
3865 char vmcontext[256];
3866 /* make backup copies */
3867 memcpy(old_context, chan->context, sizeof(chan->context));
3868 memcpy(old_exten, chan->exten, sizeof(chan->exten));
3869 old_priority = chan->priority;
3871 /* call the the Directory, changes the channel */
3872 sprintf(vmcontext, "%s||v", context ? context : "default");
3873 res = pbx_exec(chan, app, vmcontext);
3875 ast_copy_string(username, chan->exten, sizeof(username));
3877 /* restore the old context, exten, and priority */
3878 memcpy(chan->context, old_context, sizeof(chan->context));
3879 memcpy(chan->exten, old_exten, sizeof(chan->exten));
3880 chan->priority = old_priority;
3882 } else {
3883 ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
3884 ast_clear_flag((&globalflags), VM_DIRECFORWARD);
3886 } else {
3887 /* Ask for an extension */
3888 res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
3889 if (res)
3890 break;
3891 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
3892 break;
3895 /* start all over if no username */
3896 if (ast_strlen_zero(username))
3897 continue;
3898 stringp = username;
3899 s = strsep(&stringp, "*");
3900 /* start optimistic */
3901 valid_extensions = 1;
3902 while (s) {
3903 /* Don't forward to ourselves. find_user is going to malloc since we have a NULL as first argument */
3904 if (strcmp(s,sender->mailbox) && (receiver = find_user(NULL, context, s))) {
3905 AST_LIST_INSERT_HEAD(&extensions, receiver, list);
3906 found++;
3907 } else {
3908 valid_extensions = 0;
3909 break;
3911 s = strsep(&stringp, "*");
3913 /* break from the loop of reading the extensions */
3914 if (valid_extensions)
3915 break;
3916 /* "I am sorry, that's not a valid extension. Please try again." */
3917 res = ast_play_and_wait(chan, "pbx-invalid");
3919 /* check if we're clear to proceed */
3920 if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
3921 return res;
3922 if (flag==1) {
3923 struct leave_vm_options leave_options;
3924 char mailbox[AST_MAX_EXTENSION * 2 + 2];
3925 snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
3927 /* Send VoiceMail */
3928 memset(&leave_options, 0, sizeof(leave_options));
3929 leave_options.record_gain = record_gain;
3930 cmd = leave_voicemail(chan, mailbox, &leave_options);
3931 } else {
3933 /* Forward VoiceMail */
3934 RETRIEVE(dir, curmsg);
3935 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context, record_gain);
3936 if (!cmd) {
3937 AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
3938 #ifdef IMAP_STORAGE
3939 /* Need to get message content */
3940 if(option_debug > 2)
3941 ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
3942 if (vms->msgArray[vms->curmsg] == 0) {
3943 ast_log (LOG_WARNING,"Trying to access unknown message\n");
3944 return -1;
3947 /* This will only work for new messages... */
3948 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
3949 /* empty string means no valid header */
3950 if (ast_strlen(header_content)) {
3951 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
3952 return -1;
3954 /* Get header info needed by sendmail */
3955 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:");
3956 if (temp)
3957 duration = atoi(temp);
3958 else
3959 duration = 0;
3961 /* Attach only the first format */
3962 fmt = ast_strdupa(fmt);
3963 if (fmt) {
3964 stringp = fmt;
3965 strsep(&stringp, "|");
3966 } else {
3967 ast_log (LOG_ERROR,"audio format not set. Default to WAV\n");
3968 fmt = "WAV";
3970 if (!strcasecmp(fmt, "wav49"))
3971 fmt = "WAV";
3972 if(option_debug > 2)
3973 ast_log (LOG_DEBUG,"**** format set to %s, vmfmts set to %s\n",fmt,vmfmts);
3974 /* ast_copy_string(fmt, vmfmts, sizeof(fmt));*/
3975 /* if (!ast_strlen_zero(fmt)) { */
3976 snprintf(todir, sizeof(todir), "%s/imap", VM_SPOOL_DIR);
3977 make_gsm_file(vms->fn, vms->imapuser, todir, vms->curmsg);
3978 if(option_debug > 2)
3979 ast_log (LOG_DEBUG,"Before mail_fetchstructure, message number is %ld, filename is:%s\n",vms->msgArray[vms->curmsg], vms->fn);
3980 /*mail_fetchstructure (mailstream, vmArray[0], &body); */
3981 mail_fetchstructure (vms->mailstream, vms->msgArray[vms->curmsg], &body);
3982 save_body(body,vms,"3","gsm");
3983 /* should not assume "fmt" here! */
3984 save_body(body,vms,"2",fmt);
3986 char *myserveremail = serveremail;
3987 if (!ast_strlen_zero(vmtmp->serveremail))
3988 myserveremail = vmtmp->serveremail;
3989 int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
3990 attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
3991 sprintf(fn, "%s/%s/msg%04d", todir, vms->imapuser, vms->curmsg);
3992 /* NULL category for IMAP storage */
3993 sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox, chan->cid.cid_num, chan->cid.cid_name, fn, fmt, duration, attach_user_voicemail, chan, NULL);
3994 #else
3996 /* if (ast_play_and_wait(chan, "vm-savedto"))
3997 break;
3999 snprintf(todir, sizeof(todir), "%s%s/%s/INBOX", VM_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
4000 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
4001 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmtmp->mailbox, vmtmp->context);
4002 ast_safe_system(sys);
4004 res = count_messages(receiver, todir);
4006 if ( (res == ERROR_LOCK_PATH) || (res < 0) ) {
4007 if (res == ERROR_LOCK_PATH)
4008 ast_log(LOG_WARNING, "Unable to lock the directory %s to forward the requested vmail msg!\n", todir);
4009 else
4010 ast_log(LOG_WARNING, "Unable to determine how many msgs are in the destination folder!\n");
4011 break;
4013 todircount = res;
4014 ast_copy_string(tmp, fmt, sizeof(tmp));
4015 stringp = tmp;
4016 while ((s = strsep(&stringp, "|"))) {
4017 /* XXX This is a hack -- we should use build_filename or similar XXX */
4018 if (!strcasecmp(s, "wav49"))
4019 s = "WAV";
4020 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
4021 ast_safe_system(sys);
4023 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
4024 ast_safe_system(sys);
4025 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
4027 STORE(todir, vmtmp->mailbox, vmtmp->context, todircount);
4029 /* load the information on the source message so we can send an e-mail like a new message */
4030 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
4031 if ((mif=ast_config_load(miffile))) {
4032 const char *category = ast_variable_retrieve(mif, NULL, "category");
4034 /* set callerid and duration variables */
4035 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
4036 s = ast_variable_retrieve(mif, NULL, "duration");
4037 if (s)
4038 duration = atoi(s);
4039 else
4040 duration = 0;
4041 if (!ast_strlen_zero(vmtmp->email)) {
4042 int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
4043 char *myserveremail = serveremail;
4044 attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
4045 if (!ast_strlen_zero(vmtmp->serveremail))
4046 myserveremail = vmtmp->serveremail;
4047 /*XXX POSSIBLE ISSUE FOR IMAP STORAGE but no idea how its an else statement XXX*/
4048 sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox, chan->cid.cid_num, chan->cid.cid_name, fn, tmp, duration, attach_user_voicemail, chan, category);
4051 if (!ast_strlen_zero(vmtmp->pager)) {
4052 char *myserveremail = serveremail;
4053 if (!ast_strlen_zero(vmtmp->serveremail))
4054 myserveremail = vmtmp->serveremail;
4055 sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->context, vmtmp->mailbox, chan->cid.cid_num, chan->cid.cid_name, duration, vmtmp, category);
4058 ast_config_destroy(mif); /* or here */
4060 /* Leave voicemail for someone */
4061 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
4062 run_externnotify(vmtmp->context, vmtmp->mailbox);
4063 #endif
4064 saved_messages++;
4065 AST_LIST_REMOVE_CURRENT(&extensions, list);
4066 free_user(vmtmp);
4067 if (res)
4068 break;
4070 AST_LIST_TRAVERSE_SAFE_END;
4071 if (saved_messages > 0) {
4072 /* give confirmation that the message was saved */
4073 /* commented out since we can't forward batches yet
4074 if (saved_messages == 1)
4075 res = ast_play_and_wait(chan, "vm-message");
4076 else
4077 res = ast_play_and_wait(chan, "vm-messages");
4078 if (!res)
4079 res = ast_play_and_wait(chan, "vm-saved"); */
4080 res = ast_play_and_wait(chan, "vm-msgsaved");
4084 return res ? res : cmd;
4087 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
4089 int res;
4090 if ((res = ast_stream_and_wait(chan, file, chan->language, AST_DIGIT_ANY)) < 0)
4091 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
4092 return res;
4095 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
4097 return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", "2", skipms);
4100 static int play_message_category(struct ast_channel *chan, char *category)
4102 int res = 0;
4104 if (!ast_strlen_zero(category))
4105 res = ast_play_and_wait(chan, category);
4107 if (res) {
4108 ast_log(LOG_WARNING, "No sound file for category '%s' was found.\n", category);
4109 res = 0;
4112 return res;
4115 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
4117 int res = 0;
4118 struct vm_zone *the_zone = NULL;
4119 time_t t;
4121 if (ast_get_time_t(origtime, &t, 0, NULL)) {
4122 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
4123 return 0;
4126 /* Does this user have a timezone specified? */
4127 if (!ast_strlen_zero(vmu->zonetag)) {
4128 /* Find the zone in the list */
4129 struct vm_zone *z;
4130 AST_LIST_LOCK(&zones);
4131 AST_LIST_TRAVERSE(&zones, z, list) {
4132 if (!strcmp(z->name, vmu->zonetag)) {
4133 the_zone = z;
4134 break;
4137 AST_LIST_UNLOCK(&zones);
4140 /* No internal variable parsing for now, so we'll comment it out for the time being */
4141 #if 0
4142 /* Set the DIFF_* variables */
4143 localtime_r(&t, &time_now);
4144 tv_now = ast_tvnow();
4145 tnow = tv_now.tv_sec;
4146 localtime_r(&tnow,&time_then);
4148 /* Day difference */
4149 if (time_now.tm_year == time_then.tm_year)
4150 snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
4151 else
4152 snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
4153 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
4155 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
4156 #endif
4157 if (the_zone)
4158 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
4159 else if (!strcasecmp(chan->language,"pl")) /* POLISH syntax */
4160 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
4161 else if (!strcasecmp(chan->language,"se")) /* SWEDISH syntax */
4162 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
4163 else if (!strcasecmp(chan->language,"no")) /* NORWEGIAN syntax */
4164 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
4165 else if (!strcasecmp(chan->language,"de")) /* GERMAN syntax */
4166 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
4167 else if (!strcasecmp(chan->language,"nl")) /* DUTCH syntax */
4168 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
4169 else if (!strcasecmp(chan->language,"it")) /* ITALIAN syntax */
4170 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);
4171 else if (!strcasecmp(chan->language,"gr"))
4172 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q H 'digits/kai' M ", NULL);
4173 else
4174 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
4175 #if 0
4176 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
4177 #endif
4178 return res;
4183 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
4185 int res = 0;
4186 int i;
4187 char *callerid, *name;
4188 char prefile[256]="";
4191 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
4192 /* 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 */
4193 if ((cid == NULL)||(context == NULL))
4194 return res;
4196 /* Strip off caller ID number from name */
4197 ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
4198 ast_callerid_parse(cid, &name, &callerid);
4199 if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
4200 /* Check for internal contexts and only */
4201 /* say extension when the call didn't come from an internal context in the list */
4202 for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
4203 ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
4204 if ((strcmp(cidinternalcontexts[i], context) == 0))
4205 break;
4207 if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
4208 if (!res) {
4209 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
4210 if (!ast_strlen_zero(prefile)) {
4211 /* See if we can find a recorded name for this person instead of their extension number */
4212 if (ast_fileexists(prefile, NULL, NULL) > 0) {
4213 if (option_verbose > 2)
4214 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
4215 if (!callback)
4216 res = wait_file2(chan, vms, "vm-from");
4217 res = ast_stream_and_wait(chan, prefile, chan->language, "");
4218 } else {
4219 if (option_verbose > 2)
4220 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
4221 /* BB: Say "from extension" as one saying to sound smoother */
4222 if (!callback)
4223 res = wait_file2(chan, vms, "vm-from-extension");
4224 res = ast_say_digit_str(chan, callerid, "", chan->language);
4230 else if (!res){
4231 ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
4232 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
4233 if (!callback)
4234 res = wait_file2(chan, vms, "vm-from-phonenumber");
4235 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
4237 } else {
4238 /* Number unknown */
4239 ast_log(LOG_DEBUG, "VM-CID: From an unknown number\n");
4240 /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
4241 res = wait_file2(chan, vms, "vm-unknown-caller");
4243 return res;
4246 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, char *duration, int minduration)
4248 int res = 0;
4249 int durationm;
4250 int durations;
4251 /* Verify that we have a duration for the message */
4252 if (duration == NULL)
4253 return res;
4255 /* Convert from seconds to minutes */
4256 durations=atoi(duration);
4257 durationm=(durations / 60);
4259 ast_log(LOG_DEBUG, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
4261 if ((!res) && (durationm >= minduration)) {
4262 res = wait_file2(chan, vms, "vm-duration");
4264 /* POLISH syntax */
4265 if (!strcasecmp(chan->language, "pl")) {
4266 div_t num = div(durationm, 10);
4268 if (durationm == 1) {
4269 res = ast_play_and_wait(chan, "digits/1z");
4270 res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
4271 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
4272 if (num.rem == 2) {
4273 if (!num.quot) {
4274 res = ast_play_and_wait(chan, "digits/2-ie");
4275 } else {
4276 res = say_and_wait(chan, durationm - 2 , chan->language);
4277 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
4279 } else {
4280 res = say_and_wait(chan, durationm, chan->language);
4282 res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
4283 } else {
4284 res = say_and_wait(chan, durationm, chan->language);
4285 res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
4287 /* DEFAULT syntax */
4288 } else {
4289 res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
4290 res = wait_file2(chan, vms, "vm-minutes");
4293 return res;
4296 #ifdef IMAP_STORAGE
4297 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
4299 BODY *body;
4300 char *header_content;
4301 char cid[256];
4302 char context[256];
4303 char origtime[32];
4304 char duration[16];
4305 char category[32];
4306 char todir[256];
4307 int res = 0;
4308 char *temp;
4310 vms->starting = 0;
4311 if(option_debug > 2)
4312 ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
4313 if (vms->msgArray[vms->curmsg] == 0) {
4314 ast_log (LOG_WARNING,"Trying to access unknown message\n");
4315 return -1;
4318 /* This will only work for new messages... */
4319 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
4320 /* empty string means no valid header */
4321 if (ast_strlen(header_content)) {
4322 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
4323 return -1;
4325 snprintf(todir, sizeof(todir), "%s/imap", VM_SPOOL_DIR);
4326 make_gsm_file(vms->fn, vms->imapuser, todir, vms->curmsg);
4328 mail_fetchstructure (vms->mailstream,vms->msgArray[vms->curmsg],&body);
4329 save_body(body,vms,"3","gsm");
4331 adsi_message(chan, vms);
4332 if (!vms->curmsg)
4333 res = wait_file2(chan, vms, "vm-first"); /* "First" */
4334 else if (vms->curmsg == vms->lastmsg)
4335 res = wait_file2(chan, vms, "vm-last"); /* "last" */
4336 if (!res) {
4337 res = wait_file2(chan, vms, "vm-message"); /* "message" */
4338 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
4339 if (!res)
4340 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
4344 /* Get info from headers!! */
4345 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:");
4347 if (temp)
4348 strcpy(cid,temp);
4349 else
4350 cid[0] = '\0';
4352 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Context:");
4354 if (temp)
4355 strcpy(context,temp);
4356 else
4357 context[0] = '\0';
4359 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:");
4361 if (temp)
4362 strcpy(origtime,temp);
4363 else
4364 origtime[0] = '\0';
4366 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:");
4368 if (temp)
4369 strcpy(duration,temp);
4370 else
4371 duration[0] = '\0';
4373 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Category:");
4375 if (temp)
4376 strcpy(category,temp);
4377 else
4378 category[0] = '\0';
4380 /*if (!strncasecmp("macro",context,5)) Macro names in contexts are useless for our needs */
4381 /* context = ast_variable_retrieve(msg_cfg, "message","macrocontext"); */
4382 if (res == '1')
4383 res = 0;
4385 if ((!res) && !ast_strlen_zero(category)) {
4386 res = play_message_category(chan, category);
4389 if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)) && origtime[0] != '\0')
4390 res = play_message_datetime(chan, vmu, origtime, "IMAP_STORAGE");
4391 if ((!res) && (ast_test_flag(vmu, VM_SAYCID)) && cid[0] !='\0' && context[0] !='\0')
4392 res = play_message_callerid(chan, vms, cid, context, 0);
4394 if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)) && duration[0] != '\0')
4395 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
4397 /* Allow pressing '1' to skip envelope / callerid */
4398 /* if (res == '1')
4399 res = 0;
4401 /*ast_config_destroy(msg_cfg);*/
4402 res = 0;
4404 if (!res) {
4405 vms->heard[vms->curmsg] = 1;
4406 res = wait_file(chan, vms, vms->fn);
4408 DISPOSE(vms->curdir, vms->curmsg);
4409 return res;
4411 #else
4412 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
4414 int res = 0;
4415 char filename[256],*origtime, *cid, *context, *duration;
4416 char *category;
4417 struct ast_config *msg_cfg;
4419 vms->starting = 0;
4420 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
4421 adsi_message(chan, vms);
4422 if (!vms->curmsg)
4423 res = wait_file2(chan, vms, "vm-first"); /* "First" */
4424 else if (vms->curmsg == vms->lastmsg)
4425 res = wait_file2(chan, vms, "vm-last"); /* "last" */
4426 if (!res) {
4427 /* POLISH syntax */
4428 if (!strcasecmp(chan->language, "pl")) {
4429 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
4430 int ten, one;
4431 char nextmsg[256];
4432 ten = (vms->curmsg + 1) / 10;
4433 one = (vms->curmsg + 1) % 10;
4435 if (vms->curmsg < 20) {
4436 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
4437 res = wait_file2(chan, vms, nextmsg);
4438 } else {
4439 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
4440 res = wait_file2(chan, vms, nextmsg);
4441 if (one > 0) {
4442 if (!res) {
4443 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
4444 res = wait_file2(chan, vms, nextmsg);
4449 if (!res)
4450 res = wait_file2(chan, vms, "vm-message");
4451 } else {
4452 if (!strcasecmp(chan->language, "se")) /* SWEDISH syntax */
4453 res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
4454 else /* DEFAULT syntax */
4455 res = wait_file2(chan, vms, "vm-message");
4456 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
4457 if (!res)
4458 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
4463 /* Retrieve info from VM attribute file */
4464 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
4465 snprintf(filename, sizeof(filename), "%s.txt", vms->fn2);
4466 RETRIEVE(vms->curdir, vms->curmsg);
4467 msg_cfg = ast_config_load(filename);
4468 if (!msg_cfg) {
4469 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
4470 return 0;
4473 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
4474 ast_log(LOG_WARNING, "No origtime?!\n");
4475 DISPOSE(vms->curdir, vms->curmsg);
4476 ast_config_destroy(msg_cfg);
4477 return 0;
4480 cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
4481 duration = ast_variable_retrieve(msg_cfg, "message", "duration");
4482 category = ast_variable_retrieve(msg_cfg, "message", "category");
4484 context = ast_variable_retrieve(msg_cfg, "message", "context");
4485 if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
4486 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
4487 if (!res)
4488 res = play_message_category(chan, category);
4489 if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
4490 res = play_message_datetime(chan, vmu, origtime, filename);
4491 if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
4492 res = play_message_callerid(chan, vms, cid, context, 0);
4493 if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
4494 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
4495 /* Allow pressing '1' to skip envelope / callerid */
4496 if (res == '1')
4497 res = 0;
4498 ast_config_destroy(msg_cfg);
4500 if (!res) {
4501 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
4502 vms->heard[vms->curmsg] = 1;
4503 res = wait_file(chan, vms, vms->fn);
4505 DISPOSE(vms->curdir, vms->curmsg);
4506 return res;
4508 #endif
4510 #ifdef IMAP_STORAGE
4511 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
4513 SEARCHPGM *pgm;
4514 SEARCHHEADER *hdr;
4515 int ret;
4517 strcpy(vms->imapuser,vmu->imapuser);
4518 if (box == 1) {
4519 ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
4520 sprintf(vms->vmbox, "vm-%s", mbox(1));
4521 } else {
4522 ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
4523 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
4526 if(option_debug > 2)
4527 ast_log(LOG_DEBUG,"Before init_mailstream, user is %s\n",vmu->imapuser);
4528 ret = init_mailstream(vms);
4529 if (ret != 0) {
4530 ast_log (LOG_ERROR,"Could not initialize mailstream\n");
4531 return -1;
4534 /* Check Quota (here for now to test) */
4535 mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
4536 imap_getquotaroot(vms->mailstream,"INBOX");
4538 pgm = mail_newsearchpgm();
4540 /* Check IMAP folder for Asterisk messages only... */
4541 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", vmu->mailbox);
4542 pgm->header = hdr;
4543 pgm->deleted = 0;
4544 pgm->undeleted = 1;
4546 /* if box = 0, check for new, if box = 1, check for read */
4547 if (box == 0) {
4548 pgm->unseen = 1;
4549 pgm->seen = 0;
4550 } else if (box == 1) {
4551 pgm->seen = 1;
4552 pgm->unseen = 0;
4555 vms->vmArrayIndex = 0;
4556 if(option_debug > 2)
4557 ast_log(LOG_DEBUG,"Before mail_search_full, user is %s\n",vmu->imapuser);
4558 mail_search_full (vms->mailstream, NULL, pgm, NIL);
4561 vms->lastmsg = vms->vmArrayIndex - 1;
4563 return 0;
4565 #else
4566 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
4568 int res = 0;
4569 int count_msg, last_msg;
4571 ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
4573 /* Rename the member vmbox HERE so that we don't try to return before
4574 * we know what's going on.
4576 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
4578 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
4579 count_msg = count_messages(vmu, vms->curdir);
4580 if (count_msg < 0)
4581 return count_msg;
4582 else
4583 vms->lastmsg = count_msg - 1;
4586 The following test is needed in case sequencing gets messed up.
4587 There appears to be more than one way to mess up sequence, so
4588 we will not try to find all of the root causes--just fix it when
4589 detected.
4592 last_msg = last_message_index(vmu, vms->curdir);
4593 if (last_msg < 0)
4594 return last_msg;
4595 else if (vms->lastmsg != last_msg)
4597 ast_log(LOG_NOTICE, "Resequencing Mailbox: %s\n", vms->curdir);
4598 res = resequence_mailbox(vmu, vms->curdir);
4599 if (res)
4600 return res;
4603 return 0;
4605 #endif
4607 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
4609 int x = 0;
4610 if (vms->lastmsg <= -1)
4611 goto done;
4613 vms->curmsg = -1;
4614 #ifndef IMAP_STORAGE
4615 int res = 0, nummsg;
4616 /* Get the deleted messages fixed */
4617 if (vm_lock_path(vms->curdir))
4618 return ERROR_LOCK_PATH;
4620 for (x = 0; x < vmu->maxmsg; x++) {
4621 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
4622 /* Save this message. It's not in INBOX or hasn't been heard */
4623 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
4624 if (!EXISTS(vms->curdir, x, vms->fn, NULL))
4625 break;
4626 vms->curmsg++;
4627 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
4628 if (strcmp(vms->fn, vms->fn2)) {
4629 RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
4631 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
4632 /* Move to old folder before deleting */
4633 res = save_to_folder(vmu, vms, x, 1);
4634 if (res == ERROR_LOCK_PATH) {
4635 /* If save failed do not delete the message */
4636 vms->deleted[x] = 0;
4637 vms->heard[x] = 0;
4638 --x;
4643 /* Delete ALL remaining messages */
4644 nummsg = x - 1;
4645 for (x = vms->curmsg + 1; x <= nummsg; x++) {
4646 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
4647 if (EXISTS(vms->curdir, x, vms->fn, NULL))
4648 DELETE(vms->curdir, x, vms->fn);
4650 ast_unlock_path(vms->curdir);
4651 #else
4652 for (x=0;x < vmu->maxmsg;x++) {
4653 if (vms->deleted[x]) {
4654 if(option_debug > 2)
4655 ast_log(LOG_DEBUG,"IMAP delete of %d\n",x);
4656 IMAP_DELETE(vms->curdir, x, vms->fn, vms);
4659 #endif
4661 done:
4662 if (vms->deleted)
4663 memset(vms->deleted, 0, vmu->maxmsg * sizeof(int));
4664 if (vms->heard)
4665 memset(vms->heard, 0, vmu->maxmsg * sizeof(int));
4667 return 0;
4670 /* In Greek even though we CAN use a syntax like "friends messages"
4671 * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
4672 * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
4673 * syntax for the above three categories which is more elegant.
4676 static int vm_play_folder_name_gr(struct ast_channel *chan, char *mbox)
4678 int cmd;
4679 char *buf;
4681 buf = alloca(strlen(mbox)+2);
4682 strcpy(buf, mbox);
4683 strcat(buf,"s");
4685 if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")){
4686 cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
4687 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
4688 } else {
4689 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
4690 return cmd ? cmd : ast_play_and_wait(chan, mbox); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
4694 static int vm_play_folder_name_pl(struct ast_channel *chan, char *mbox)
4696 int cmd;
4698 if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")) {
4699 if (!strcasecmp(mbox, "vm-INBOX"))
4700 cmd = ast_play_and_wait(chan, "vm-new-e");
4701 else
4702 cmd = ast_play_and_wait(chan, "vm-old-e");
4703 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
4704 } else {
4705 cmd = ast_play_and_wait(chan, "vm-messages");
4706 return cmd ? cmd : ast_play_and_wait(chan, mbox);
4710 static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
4712 int cmd;
4714 if (!strcasecmp(chan->language, "it") || !strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "fr") || !strcasecmp(chan->language, "pt")) { /* Italian, Spanish, French or Portuguese syntax */
4715 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
4716 return cmd ? cmd : ast_play_and_wait(chan, mbox);
4717 } else if (!strcasecmp(chan->language, "gr")){
4718 return vm_play_folder_name_gr(chan, mbox);
4719 } else if (!strcasecmp(chan->language, "pl")){
4720 return vm_play_folder_name_pl(chan, mbox);
4721 } else { /* Default English */
4722 cmd = ast_play_and_wait(chan, mbox);
4723 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
4727 /* GREEK SYNTAX
4728 In greek the plural for old/new is
4729 different so we need the following files
4730 We also need vm-denExeteMynhmata because
4731 this syntax is different.
4733 -> vm-Olds.wav : "Palia"
4734 -> vm-INBOXs.wav : "Nea"
4735 -> vm-denExeteMynhmata : "den exete mynhmata"
4739 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
4741 int res = 0;
4743 if (vms->newmessages) {
4744 res = ast_play_and_wait(chan, "vm-youhave");
4745 if (!res)
4746 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
4747 if (!res) {
4748 if ((vms->newmessages == 1)) {
4749 res = ast_play_and_wait(chan, "vm-INBOX");
4750 if (!res)
4751 res = ast_play_and_wait(chan, "vm-message");
4752 } else {
4753 res = ast_play_and_wait(chan, "vm-INBOXs");
4754 if (!res)
4755 res = ast_play_and_wait(chan, "vm-messages");
4758 } else if (vms->oldmessages){
4759 res = ast_play_and_wait(chan, "vm-youhave");
4760 if (!res)
4761 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
4762 if ((vms->oldmessages == 1)){
4763 res = ast_play_and_wait(chan, "vm-Old");
4764 if (!res)
4765 res = ast_play_and_wait(chan, "vm-message");
4766 } else {
4767 res = ast_play_and_wait(chan, "vm-Olds");
4768 if (!res)
4769 res = ast_play_and_wait(chan, "vm-messages");
4771 } else if (!vms->oldmessages && !vms->newmessages)
4772 res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
4773 return res;
4776 /* Default English syntax */
4777 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
4779 int res;
4781 /* Introduce messages they have */
4782 res = ast_play_and_wait(chan, "vm-youhave");
4783 if (!res) {
4784 if (vms->newmessages) {
4785 res = say_and_wait(chan, vms->newmessages, chan->language);
4786 if (!res)
4787 res = ast_play_and_wait(chan, "vm-INBOX");
4788 if (vms->oldmessages && !res)
4789 res = ast_play_and_wait(chan, "vm-and");
4790 else if (!res) {
4791 if ((vms->newmessages == 1))
4792 res = ast_play_and_wait(chan, "vm-message");
4793 else
4794 res = ast_play_and_wait(chan, "vm-messages");
4798 if (!res && vms->oldmessages) {
4799 res = say_and_wait(chan, vms->oldmessages, chan->language);
4800 if (!res)
4801 res = ast_play_and_wait(chan, "vm-Old");
4802 if (!res) {
4803 if (vms->oldmessages == 1)
4804 res = ast_play_and_wait(chan, "vm-message");
4805 else
4806 res = ast_play_and_wait(chan, "vm-messages");
4809 if (!res) {
4810 if (!vms->oldmessages && !vms->newmessages) {
4811 res = ast_play_and_wait(chan, "vm-no");
4812 if (!res)
4813 res = ast_play_and_wait(chan, "vm-messages");
4817 return res;
4820 /* ITALIAN syntax */
4821 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
4823 /* Introduce messages they have */
4824 int res;
4825 if (!vms->oldmessages && !vms->newmessages)
4826 res = ast_play_and_wait(chan, "vm-no") ||
4827 ast_play_and_wait(chan, "vm-message");
4828 else
4829 res = ast_play_and_wait(chan, "vm-youhave");
4830 if (!res && vms->newmessages) {
4831 res = (vms->newmessages == 1) ?
4832 ast_play_and_wait(chan, "digits/un") ||
4833 ast_play_and_wait(chan, "vm-nuovo") ||
4834 ast_play_and_wait(chan, "vm-message") :
4835 /* 2 or more new messages */
4836 say_and_wait(chan, vms->newmessages, chan->language) ||
4837 ast_play_and_wait(chan, "vm-nuovi") ||
4838 ast_play_and_wait(chan, "vm-messages");
4839 if (!res && vms->oldmessages)
4840 res = ast_play_and_wait(chan, "vm-and");
4842 if (!res && vms->oldmessages) {
4843 res = (vms->oldmessages == 1) ?
4844 ast_play_and_wait(chan, "digits/un") ||
4845 ast_play_and_wait(chan, "vm-vecchio") ||
4846 ast_play_and_wait(chan, "vm-message") :
4847 /* 2 or more old messages */
4848 say_and_wait(chan, vms->oldmessages, chan->language) ||
4849 ast_play_and_wait(chan, "vm-vecchi") ||
4850 ast_play_and_wait(chan, "vm-messages");
4852 return res ? -1 : 0;
4855 /* POLISH syntax */
4856 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
4858 /* Introduce messages they have */
4859 int res;
4860 div_t num;
4862 if (!vms->oldmessages && !vms->newmessages) {
4863 res = ast_play_and_wait(chan, "vm-no");
4864 res = res ? res : ast_play_and_wait(chan, "vm-messages");
4865 return res;
4866 } else {
4867 res = ast_play_and_wait(chan, "vm-youhave");
4870 if (vms->newmessages) {
4871 num = div(vms->newmessages, 10);
4872 if (vms->newmessages == 1) {
4873 res = ast_play_and_wait(chan, "digits/1-a");
4874 res = res ? res : ast_play_and_wait(chan, "vm-new-a");
4875 res = res ? res : ast_play_and_wait(chan, "vm-message");
4876 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
4877 if (num.rem == 2) {
4878 if (!num.quot) {
4879 res = ast_play_and_wait(chan, "digits/2-ie");
4880 } else {
4881 res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
4882 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
4884 } else {
4885 res = say_and_wait(chan, vms->newmessages, chan->language);
4887 res = res ? res : ast_play_and_wait(chan, "vm-new-e");
4888 res = res ? res : ast_play_and_wait(chan, "vm-messages");
4889 } else {
4890 res = say_and_wait(chan, vms->newmessages, chan->language);
4891 res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
4892 res = res ? res : ast_play_and_wait(chan, "vm-messages");
4894 if (!res && vms->oldmessages)
4895 res = ast_play_and_wait(chan, "vm-and");
4897 if (!res && vms->oldmessages) {
4898 num = div(vms->oldmessages, 10);
4899 if (vms->oldmessages == 1) {
4900 res = ast_play_and_wait(chan, "digits/1-a");
4901 res = res ? res : ast_play_and_wait(chan, "vm-old-a");
4902 res = res ? res : ast_play_and_wait(chan, "vm-message");
4903 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
4904 if (num.rem == 2) {
4905 if (!num.quot) {
4906 res = ast_play_and_wait(chan, "digits/2-ie");
4907 } else {
4908 res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
4909 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
4911 } else {
4912 res = say_and_wait(chan, vms->oldmessages, chan->language);
4914 res = res ? res : ast_play_and_wait(chan, "vm-old-e");
4915 res = res ? res : ast_play_and_wait(chan, "vm-messages");
4916 } else {
4917 res = say_and_wait(chan, vms->oldmessages, chan->language);
4918 res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
4919 res = res ? res : ast_play_and_wait(chan, "vm-messages");
4923 return res;
4926 /* SWEDISH syntax */
4927 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
4929 /* Introduce messages they have */
4930 int res;
4932 res = ast_play_and_wait(chan, "vm-youhave");
4933 if (res)
4934 return res;
4936 if (!vms->oldmessages && !vms->newmessages) {
4937 res = ast_play_and_wait(chan, "vm-no");
4938 res = res ? res : ast_play_and_wait(chan, "vm-messages");
4939 return res;
4942 if (vms->newmessages) {
4943 if ((vms->newmessages == 1)) {
4944 res = ast_play_and_wait(chan, "digits/ett");
4945 res = res ? res : ast_play_and_wait(chan, "vm-nytt");
4946 res = res ? res : ast_play_and_wait(chan, "vm-message");
4947 } else {
4948 res = say_and_wait(chan, vms->newmessages, chan->language);
4949 res = res ? res : ast_play_and_wait(chan, "vm-nya");
4950 res = res ? res : ast_play_and_wait(chan, "vm-messages");
4952 if (!res && vms->oldmessages)
4953 res = ast_play_and_wait(chan, "vm-and");
4955 if (!res && vms->oldmessages) {
4956 if (vms->oldmessages == 1) {
4957 res = ast_play_and_wait(chan, "digits/ett");
4958 res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
4959 res = res ? res : ast_play_and_wait(chan, "vm-message");
4960 } else {
4961 res = say_and_wait(chan, vms->oldmessages, chan->language);
4962 res = res ? res : ast_play_and_wait(chan, "vm-gamla");
4963 res = res ? res : ast_play_and_wait(chan, "vm-messages");
4967 return res;
4970 /* NORWEGIAN syntax */
4971 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
4973 /* Introduce messages they have */
4974 int res;
4976 res = ast_play_and_wait(chan, "vm-youhave");
4977 if (res)
4978 return res;
4980 if (!vms->oldmessages && !vms->newmessages) {
4981 res = ast_play_and_wait(chan, "vm-no");
4982 res = res ? res : ast_play_and_wait(chan, "vm-messages");
4983 return res;
4986 if (vms->newmessages) {
4987 if ((vms->newmessages == 1)) {
4988 res = ast_play_and_wait(chan, "digits/1");
4989 res = res ? res : ast_play_and_wait(chan, "vm-ny");
4990 res = res ? res : ast_play_and_wait(chan, "vm-message");
4991 } else {
4992 res = say_and_wait(chan, vms->newmessages, chan->language);
4993 res = res ? res : ast_play_and_wait(chan, "vm-nye");
4994 res = res ? res : ast_play_and_wait(chan, "vm-messages");
4996 if (!res && vms->oldmessages)
4997 res = ast_play_and_wait(chan, "vm-and");
4999 if (!res && vms->oldmessages) {
5000 if (vms->oldmessages == 1) {
5001 res = ast_play_and_wait(chan, "digits/1");
5002 res = res ? res : ast_play_and_wait(chan, "vm-gamel");
5003 res = res ? res : ast_play_and_wait(chan, "vm-message");
5004 } else {
5005 res = say_and_wait(chan, vms->oldmessages, chan->language);
5006 res = res ? res : ast_play_and_wait(chan, "vm-gamle");
5007 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5011 return res;
5014 /* GERMAN syntax */
5015 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
5017 /* Introduce messages they have */
5018 int res;
5019 res = ast_play_and_wait(chan, "vm-youhave");
5020 if (!res) {
5021 if (vms->newmessages) {
5022 if ((vms->newmessages == 1))
5023 res = ast_play_and_wait(chan, "digits/1F");
5024 else
5025 res = say_and_wait(chan, vms->newmessages, chan->language);
5026 if (!res)
5027 res = ast_play_and_wait(chan, "vm-INBOX");
5028 if (vms->oldmessages && !res)
5029 res = ast_play_and_wait(chan, "vm-and");
5030 else if (!res) {
5031 if ((vms->newmessages == 1))
5032 res = ast_play_and_wait(chan, "vm-message");
5033 else
5034 res = ast_play_and_wait(chan, "vm-messages");
5038 if (!res && vms->oldmessages) {
5039 if (vms->oldmessages == 1)
5040 res = ast_play_and_wait(chan, "digits/1F");
5041 else
5042 res = say_and_wait(chan, vms->oldmessages, chan->language);
5043 if (!res)
5044 res = ast_play_and_wait(chan, "vm-Old");
5045 if (!res) {
5046 if (vms->oldmessages == 1)
5047 res = ast_play_and_wait(chan, "vm-message");
5048 else
5049 res = ast_play_and_wait(chan, "vm-messages");
5052 if (!res) {
5053 if (!vms->oldmessages && !vms->newmessages) {
5054 res = ast_play_and_wait(chan, "vm-no");
5055 if (!res)
5056 res = ast_play_and_wait(chan, "vm-messages");
5060 return res;
5063 /* SPANISH syntax */
5064 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
5066 /* Introduce messages they have */
5067 int res;
5068 if (!vms->oldmessages && !vms->newmessages) {
5069 res = ast_play_and_wait(chan, "vm-youhaveno");
5070 if (!res)
5071 res = ast_play_and_wait(chan, "vm-messages");
5072 } else {
5073 res = ast_play_and_wait(chan, "vm-youhave");
5075 if (!res) {
5076 if (vms->newmessages) {
5077 if (!res) {
5078 if ((vms->newmessages == 1)) {
5079 res = ast_play_and_wait(chan, "digits/1M");
5080 if (!res)
5081 res = ast_play_and_wait(chan, "vm-message");
5082 if (!res)
5083 res = ast_play_and_wait(chan, "vm-INBOXs");
5084 } else {
5085 res = say_and_wait(chan, vms->newmessages, chan->language);
5086 if (!res)
5087 res = ast_play_and_wait(chan, "vm-messages");
5088 if (!res)
5089 res = ast_play_and_wait(chan, "vm-INBOX");
5092 if (vms->oldmessages && !res)
5093 res = ast_play_and_wait(chan, "vm-and");
5095 if (vms->oldmessages) {
5096 if (!res) {
5097 if (vms->oldmessages == 1) {
5098 res = ast_play_and_wait(chan, "digits/1M");
5099 if (!res)
5100 res = ast_play_and_wait(chan, "vm-message");
5101 if (!res)
5102 res = ast_play_and_wait(chan, "vm-Olds");
5103 } else {
5104 res = say_and_wait(chan, vms->oldmessages, chan->language);
5105 if (!res)
5106 res = ast_play_and_wait(chan, "vm-messages");
5107 if (!res)
5108 res = ast_play_and_wait(chan, "vm-Old");
5113 return res;
5116 /* FRENCH syntax */
5117 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
5119 /* Introduce messages they have */
5120 int res;
5121 res = ast_play_and_wait(chan, "vm-youhave");
5122 if (!res) {
5123 if (vms->newmessages) {
5124 res = say_and_wait(chan, vms->newmessages, chan->language);
5125 if (!res)
5126 res = ast_play_and_wait(chan, "vm-INBOX");
5127 if (vms->oldmessages && !res)
5128 res = ast_play_and_wait(chan, "vm-and");
5129 else if (!res) {
5130 if ((vms->newmessages == 1))
5131 res = ast_play_and_wait(chan, "vm-message");
5132 else
5133 res = ast_play_and_wait(chan, "vm-messages");
5137 if (!res && vms->oldmessages) {
5138 res = say_and_wait(chan, vms->oldmessages, chan->language);
5139 if (!res) {
5140 if (vms->oldmessages == 1)
5141 res = ast_play_and_wait(chan, "vm-message");
5142 else
5143 res = ast_play_and_wait(chan, "vm-messages");
5145 if (!res)
5146 res = ast_play_and_wait(chan, "vm-Old");
5148 if (!res) {
5149 if (!vms->oldmessages && !vms->newmessages) {
5150 res = ast_play_and_wait(chan, "vm-no");
5151 if (!res)
5152 res = ast_play_and_wait(chan, "vm-messages");
5156 return res;
5159 /* DUTCH syntax */
5160 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
5162 /* Introduce messages they have */
5163 int res;
5164 res = ast_play_and_wait(chan, "vm-youhave");
5165 if (!res) {
5166 if (vms->newmessages) {
5167 res = say_and_wait(chan, vms->newmessages, chan->language);
5168 if (!res) {
5169 if (vms->newmessages == 1)
5170 res = ast_play_and_wait(chan, "vm-INBOXs");
5171 else
5172 res = ast_play_and_wait(chan, "vm-INBOX");
5174 if (vms->oldmessages && !res)
5175 res = ast_play_and_wait(chan, "vm-and");
5176 else if (!res) {
5177 if ((vms->newmessages == 1))
5178 res = ast_play_and_wait(chan, "vm-message");
5179 else
5180 res = ast_play_and_wait(chan, "vm-messages");
5184 if (!res && vms->oldmessages) {
5185 res = say_and_wait(chan, vms->oldmessages, chan->language);
5186 if (!res) {
5187 if (vms->oldmessages == 1)
5188 res = ast_play_and_wait(chan, "vm-Olds");
5189 else
5190 res = ast_play_and_wait(chan, "vm-Old");
5192 if (!res) {
5193 if (vms->oldmessages == 1)
5194 res = ast_play_and_wait(chan, "vm-message");
5195 else
5196 res = ast_play_and_wait(chan, "vm-messages");
5199 if (!res) {
5200 if (!vms->oldmessages && !vms->newmessages) {
5201 res = ast_play_and_wait(chan, "vm-no");
5202 if (!res)
5203 res = ast_play_and_wait(chan, "vm-messages");
5207 return res;
5210 /* PORTUGUESE syntax */
5211 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
5213 /* Introduce messages they have */
5214 int res;
5215 res = ast_play_and_wait(chan, "vm-youhave");
5216 if (!res) {
5217 if (vms->newmessages) {
5218 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
5219 if (!res) {
5220 if ((vms->newmessages == 1)) {
5221 res = ast_play_and_wait(chan, "vm-message");
5222 if (!res)
5223 res = ast_play_and_wait(chan, "vm-INBOXs");
5224 } else {
5225 res = ast_play_and_wait(chan, "vm-messages");
5226 if (!res)
5227 res = ast_play_and_wait(chan, "vm-INBOX");
5230 if (vms->oldmessages && !res)
5231 res = ast_play_and_wait(chan, "vm-and");
5233 if (!res && vms->oldmessages) {
5234 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
5235 if (!res) {
5236 if (vms->oldmessages == 1) {
5237 res = ast_play_and_wait(chan, "vm-message");
5238 if (!res)
5239 res = ast_play_and_wait(chan, "vm-Olds");
5240 } else {
5241 res = ast_play_and_wait(chan, "vm-messages");
5242 if (!res)
5243 res = ast_play_and_wait(chan, "vm-Old");
5247 if (!res) {
5248 if (!vms->oldmessages && !vms->newmessages) {
5249 res = ast_play_and_wait(chan, "vm-no");
5250 if (!res)
5251 res = ast_play_and_wait(chan, "vm-messages");
5255 return res;
5259 /* CZECH syntax */
5260 /* in czech there must be declension of word new and message
5261 * czech : english : czech : english
5262 * --------------------------------------------------------
5263 * vm-youhave : you have
5264 * vm-novou : one new : vm-zpravu : message
5265 * vm-nove : 2-4 new : vm-zpravy : messages
5266 * vm-novych : 5-infinite new : vm-zprav : messages
5267 * vm-starou : one old
5268 * vm-stare : 2-4 old
5269 * vm-starych : 5-infinite old
5270 * jednu : one - falling 4.
5271 * vm-no : no ( no messages )
5274 static int vm_intro_cz(struct ast_channel *chan,struct vm_state *vms)
5276 int res;
5277 res = ast_play_and_wait(chan, "vm-youhave");
5278 if (!res) {
5279 if (vms->newmessages) {
5280 if (vms->newmessages == 1) {
5281 res = ast_play_and_wait(chan, "digits/jednu");
5282 } else {
5283 res = say_and_wait(chan, vms->newmessages, chan->language);
5285 if (!res) {
5286 if ((vms->newmessages == 1))
5287 res = ast_play_and_wait(chan, "vm-novou");
5288 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
5289 res = ast_play_and_wait(chan, "vm-nove");
5290 if (vms->newmessages > 4)
5291 res = ast_play_and_wait(chan, "vm-novych");
5293 if (vms->oldmessages && !res)
5294 res = ast_play_and_wait(chan, "vm-and");
5295 else if (!res) {
5296 if ((vms->newmessages == 1))
5297 res = ast_play_and_wait(chan, "vm-zpravu");
5298 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
5299 res = ast_play_and_wait(chan, "vm-zpravy");
5300 if (vms->newmessages > 4)
5301 res = ast_play_and_wait(chan, "vm-zprav");
5304 if (!res && vms->oldmessages) {
5305 res = say_and_wait(chan, vms->oldmessages, chan->language);
5306 if (!res) {
5307 if ((vms->oldmessages == 1))
5308 res = ast_play_and_wait(chan, "vm-starou");
5309 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
5310 res = ast_play_and_wait(chan, "vm-stare");
5311 if (vms->oldmessages > 4)
5312 res = ast_play_and_wait(chan, "vm-starych");
5314 if (!res) {
5315 if ((vms->oldmessages == 1))
5316 res = ast_play_and_wait(chan, "vm-zpravu");
5317 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
5318 res = ast_play_and_wait(chan, "vm-zpravy");
5319 if (vms->oldmessages > 4)
5320 res = ast_play_and_wait(chan, "vm-zprav");
5323 if (!res) {
5324 if (!vms->oldmessages && !vms->newmessages) {
5325 res = ast_play_and_wait(chan, "vm-no");
5326 if (!res)
5327 res = ast_play_and_wait(chan, "vm-zpravy");
5331 return res;
5334 static int get_lastdigits(int num)
5336 num %= 100;
5337 return (num < 20) ? num : num % 10;
5340 static int vm_intro_ru(struct ast_channel *chan,struct vm_state *vms)
5342 int res;
5343 int lastnum = 0;
5344 int dcnum;
5346 res = ast_play_and_wait(chan, "vm-youhave");
5347 if (!res && vms->newmessages) {
5348 lastnum = get_lastdigits(vms->newmessages);
5349 dcnum = vms->newmessages - lastnum;
5350 if (dcnum)
5351 res = say_and_wait(chan, dcnum, chan->language);
5352 if (!res && lastnum) {
5353 if (lastnum == 1)
5354 res = ast_play_and_wait(chan, "digits/ru/odno");
5355 else
5356 res = say_and_wait(chan, lastnum, chan->language);
5359 if (!res)
5360 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-novoe" : "vm-novyh");
5362 if (!res && vms->oldmessages)
5363 res = ast_play_and_wait(chan, "vm-and");
5366 if (!res && vms->oldmessages) {
5367 lastnum = get_lastdigits(vms->oldmessages);
5368 dcnum = vms->newmessages - lastnum;
5369 if (dcnum)
5370 res = say_and_wait(chan, dcnum, chan->language);
5371 if (!res && lastnum) {
5372 if (lastnum == 1)
5373 res = ast_play_and_wait(chan, "digits/ru/odno");
5374 else
5375 res = say_and_wait(chan, lastnum, chan->language);
5378 if (!res)
5379 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-staroe" : "vm-staryh");
5382 if (!res && !vms->newmessages && !vms->oldmessages) {
5383 lastnum = 0;
5384 res = ast_play_and_wait(chan, "vm-no");
5387 if (!res) {
5388 switch (lastnum) {
5389 case 1:
5390 res = ast_play_and_wait(chan, "vm-soobshenie");
5391 break;
5392 case 2:
5393 case 3:
5394 case 4:
5395 res = ast_play_and_wait(chan, "vm-soobsheniya");
5396 break;
5397 default:
5398 res = ast_play_and_wait(chan, "vm-soobsheniy");
5399 break;
5403 return res;
5407 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
5409 char prefile[256];
5411 /* Notify the user that the temp greeting is set and give them the option to remove it */
5412 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
5413 if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
5414 if (ast_fileexists(prefile, NULL, NULL) > 0)
5415 ast_play_and_wait(chan, "vm-tempgreetactive");
5418 /* Play voicemail intro - syntax is different for different languages */
5419 if (!strcasecmp(chan->language, "de")) { /* GERMAN syntax */
5420 return vm_intro_de(chan, vms);
5421 } else if (!strcasecmp(chan->language, "es")) { /* SPANISH syntax */
5422 return vm_intro_es(chan, vms);
5423 } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN syntax */
5424 return vm_intro_it(chan, vms);
5425 } else if (!strcasecmp(chan->language, "fr")) { /* FRENCH syntax */
5426 return vm_intro_fr(chan, vms);
5427 } else if (!strcasecmp(chan->language, "nl")) { /* DUTCH syntax */
5428 return vm_intro_nl(chan, vms);
5429 } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE syntax */
5430 return vm_intro_pt(chan, vms);
5431 } else if (!strcasecmp(chan->language, "cz")) { /* CZECH syntax */
5432 return vm_intro_cz(chan, vms);
5433 } else if (!strcasecmp(chan->language, "gr")) { /* GREEK syntax */
5434 return vm_intro_gr(chan, vms);
5435 } else if (!strcasecmp(chan->language, "pl")) { /* POLISH syntax */
5436 return vm_intro_pl(chan, vms);
5437 } else if (!strcasecmp(chan->language, "se")) { /* SWEDISH syntax */
5438 return vm_intro_se(chan, vms);
5439 } else if (!strcasecmp(chan->language, "no")) { /* NORWEGIAN syntax */
5440 return vm_intro_no(chan, vms);
5441 } else if (!strcasecmp(chan->language, "ru")) { /* RUSSIAN syntax */
5442 return vm_intro_ru(chan, vms);
5443 } else { /* Default to ENGLISH */
5444 return vm_intro_en(chan, vms);
5448 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
5450 int res = 0;
5451 /* Play instructions and wait for new command */
5452 while (!res) {
5453 if (vms->starting) {
5454 if (vms->lastmsg > -1) {
5455 res = ast_play_and_wait(chan, "vm-onefor");
5456 if (!res)
5457 res = vm_play_folder_name(chan, vms->vmbox);
5459 if (!res)
5460 res = ast_play_and_wait(chan, "vm-opts");
5461 } else {
5462 if (vms->curmsg)
5463 res = ast_play_and_wait(chan, "vm-prev");
5464 if (!res && !skipadvanced)
5465 res = ast_play_and_wait(chan, "vm-advopts");
5466 if (!res)
5467 res = ast_play_and_wait(chan, "vm-repeat");
5468 if (!res && (vms->curmsg != vms->lastmsg))
5469 res = ast_play_and_wait(chan, "vm-next");
5470 if (!res) {
5471 if (!vms->deleted[vms->curmsg])
5472 res = ast_play_and_wait(chan, "vm-delete");
5473 else
5474 res = ast_play_and_wait(chan, "vm-undelete");
5475 if (!res)
5476 res = ast_play_and_wait(chan, "vm-toforward");
5477 if (!res)
5478 res = ast_play_and_wait(chan, "vm-savemessage");
5481 if (!res)
5482 res = ast_play_and_wait(chan, "vm-helpexit");
5483 if (!res)
5484 res = ast_waitfordigit(chan, 6000);
5485 if (!res) {
5486 vms->repeats++;
5487 if (vms->repeats > 2) {
5488 res = 't';
5492 return res;
5495 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
5497 int cmd = 0;
5498 int duration = 0;
5499 int tries = 0;
5500 char newpassword[80] = "";
5501 char newpassword2[80] = "";
5502 char prefile[256]="";
5503 unsigned char buf[256];
5504 int bytes=0;
5506 if (adsi_available(chan)) {
5507 bytes += adsi_logo(buf + bytes);
5508 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
5509 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
5510 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5511 bytes += adsi_voice_mode(buf + bytes, 0);
5512 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5515 /* First, have the user change their password
5516 so they won't get here again */
5517 for (;;) {
5518 newpassword[1] = '\0';
5519 newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
5520 if (cmd == '#')
5521 newpassword[0] = '\0';
5522 if (cmd < 0 || cmd == 't' || cmd == '#')
5523 return cmd;
5524 cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
5525 if (cmd < 0 || cmd == 't' || cmd == '#')
5526 return cmd;
5527 newpassword2[1] = '\0';
5528 newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
5529 if (cmd == '#')
5530 newpassword2[0] = '\0';
5531 if (cmd < 0 || cmd == 't' || cmd == '#')
5532 return cmd;
5533 cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#");
5534 if (cmd < 0 || cmd == 't' || cmd == '#')
5535 return cmd;
5536 if (!strcmp(newpassword, newpassword2))
5537 break;
5538 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
5539 cmd = ast_play_and_wait(chan, "vm-mismatch");
5540 if (++tries == 3)
5541 return -1;
5543 if (ast_strlen_zero(ext_pass_cmd))
5544 vm_change_password(vmu,newpassword);
5545 else
5546 vm_change_password_shell(vmu,newpassword);
5547 ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
5548 cmd = ast_play_and_wait(chan,"vm-passchanged");
5550 /* If forcename is set, have the user record their name */
5551 if (ast_test_flag(vmu, VM_FORCENAME)) {
5552 snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
5553 cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
5554 if (cmd < 0 || cmd == 't' || cmd == '#')
5555 return cmd;
5558 /* If forcegreetings is set, have the user record their greetings */
5559 if (ast_test_flag(vmu, VM_FORCEGREET)) {
5560 snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
5561 cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
5562 if (cmd < 0 || cmd == 't' || cmd == '#')
5563 return cmd;
5564 snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
5565 cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
5566 if (cmd < 0 || cmd == 't' || cmd == '#')
5567 return cmd;
5570 return cmd;
5573 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
5575 int cmd = 0;
5576 int retries = 0;
5577 int duration = 0;
5578 char newpassword[80] = "";
5579 char newpassword2[80] = "";
5580 char prefile[256]="";
5581 unsigned char buf[256];
5582 int bytes=0;
5584 if (adsi_available(chan))
5586 bytes += adsi_logo(buf + bytes);
5587 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
5588 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
5589 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5590 bytes += adsi_voice_mode(buf + bytes, 0);
5591 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5593 while ((cmd >= 0) && (cmd != 't')) {
5594 if (cmd)
5595 retries = 0;
5596 switch (cmd) {
5597 case '1':
5598 snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
5599 cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
5600 break;
5601 case '2':
5602 snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
5603 cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
5604 break;
5605 case '3':
5606 snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
5607 cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
5608 break;
5609 case '4':
5610 cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
5611 break;
5612 case '5':
5613 if (vmu->password[0] == '-') {
5614 cmd = ast_play_and_wait(chan, "vm-no");
5615 break;
5617 newpassword[1] = '\0';
5618 newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
5619 if (cmd == '#')
5620 newpassword[0] = '\0';
5621 else {
5622 if (cmd < 0)
5623 break;
5624 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
5625 break;
5628 newpassword2[1] = '\0';
5629 newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
5630 if (cmd == '#')
5631 newpassword2[0] = '\0';
5632 else {
5633 if (cmd < 0)
5634 break;
5636 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
5637 break;
5640 if (strcmp(newpassword, newpassword2)) {
5641 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
5642 cmd = ast_play_and_wait(chan, "vm-mismatch");
5643 break;
5645 if (ast_strlen_zero(ext_pass_cmd))
5646 vm_change_password(vmu,newpassword);
5647 else
5648 vm_change_password_shell(vmu,newpassword);
5649 ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
5650 cmd = ast_play_and_wait(chan,"vm-passchanged");
5651 break;
5652 case '*':
5653 cmd = 't';
5654 break;
5655 default:
5656 cmd = ast_play_and_wait(chan,"vm-options");
5657 if (!cmd)
5658 cmd = ast_waitfordigit(chan,6000);
5659 if (!cmd)
5660 retries++;
5661 if (retries > 3)
5662 cmd = 't';
5665 if (cmd == 't')
5666 cmd = 0;
5667 return cmd;
5670 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
5672 int cmd = 0;
5673 int retries = 0;
5674 int duration = 0;
5675 char prefile[256]="";
5676 unsigned char buf[256];
5677 int bytes=0;
5679 if (adsi_available(chan)) {
5680 bytes += adsi_logo(buf + bytes);
5681 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
5682 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
5683 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5684 bytes += adsi_voice_mode(buf + bytes, 0);
5685 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5687 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
5688 while (cmd >= 0 && cmd != 't') {
5689 if (cmd)
5690 retries = 0;
5691 RETRIEVE(prefile, -1);
5692 if (ast_fileexists(prefile, NULL, NULL) <= 0) {
5693 play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
5694 cmd = 't';
5695 } else {
5696 switch (cmd) {
5697 case '1':
5698 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
5699 break;
5700 case '2':
5701 DELETE(prefile, -1, prefile);
5702 ast_play_and_wait(chan, "vm-tempremoved");
5703 cmd = 't';
5704 break;
5705 case '*':
5706 cmd = 't';
5707 break;
5708 default:
5709 cmd = ast_play_and_wait(chan,
5710 ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
5711 "vm-tempgreeting2" : "vm-tempgreeting");
5712 if (!cmd)
5713 cmd = ast_waitfordigit(chan,6000);
5714 if (!cmd)
5715 retries++;
5716 if (retries > 3)
5717 cmd = 't';
5720 DISPOSE(prefile, -1);
5722 if (cmd == 't')
5723 cmd = 0;
5724 return cmd;
5727 /* GREEK SYNTAX */
5729 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
5731 int cmd=0;
5733 if (vms->lastmsg > -1) {
5734 cmd = play_message(chan, vmu, vms);
5735 } else {
5736 cmd = ast_play_and_wait(chan, "vm-youhaveno");
5737 if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
5738 if (!cmd) {
5739 snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
5740 cmd = ast_play_and_wait(chan, vms->fn);
5742 if (!cmd)
5743 cmd = ast_play_and_wait(chan, "vm-messages");
5744 } else {
5745 if (!cmd)
5746 cmd = ast_play_and_wait(chan, "vm-messages");
5747 if (!cmd) {
5748 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
5749 cmd = ast_play_and_wait(chan, vms->fn);
5753 return cmd;
5756 /* Default English syntax */
5757 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
5759 int cmd=0;
5761 if (vms->lastmsg > -1) {
5762 cmd = play_message(chan, vmu, vms);
5763 } else {
5764 cmd = ast_play_and_wait(chan, "vm-youhave");
5765 if (!cmd)
5766 cmd = ast_play_and_wait(chan, "vm-no");
5767 if (!cmd) {
5768 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
5769 cmd = ast_play_and_wait(chan, vms->fn);
5771 if (!cmd)
5772 cmd = ast_play_and_wait(chan, "vm-messages");
5774 return cmd;
5777 /* ITALIAN syntax */
5778 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
5780 int cmd=0;
5782 if (vms->lastmsg > -1) {
5783 cmd = play_message(chan, vmu, vms);
5784 } else {
5785 cmd = ast_play_and_wait(chan, "vm-no");
5786 if (!cmd)
5787 cmd = ast_play_and_wait(chan, "vm-message");
5788 if (!cmd) {
5789 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
5790 cmd = ast_play_and_wait(chan, vms->fn);
5793 return cmd;
5796 /* SPANISH syntax */
5797 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
5799 int cmd=0;
5801 if (vms->lastmsg > -1) {
5802 cmd = play_message(chan, vmu, vms);
5803 } else {
5804 cmd = ast_play_and_wait(chan, "vm-youhaveno");
5805 if (!cmd)
5806 cmd = ast_play_and_wait(chan, "vm-messages");
5807 if (!cmd) {
5808 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
5809 cmd = ast_play_and_wait(chan, vms->fn);
5812 return cmd;
5815 /* PORTUGUESE syntax */
5816 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
5818 int cmd=0;
5820 if (vms->lastmsg > -1) {
5821 cmd = play_message(chan, vmu, vms);
5822 } else {
5823 cmd = ast_play_and_wait(chan, "vm-no");
5824 if (!cmd) {
5825 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
5826 cmd = ast_play_and_wait(chan, vms->fn);
5828 if (!cmd)
5829 cmd = ast_play_and_wait(chan, "vm-messages");
5831 return cmd;
5834 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
5836 if (!strcasecmp(chan->language, "es")) { /* SPANISH */
5837 return vm_browse_messages_es(chan, vms, vmu);
5838 } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN */
5839 return vm_browse_messages_it(chan, vms, vmu);
5840 } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE */
5841 return vm_browse_messages_pt(chan, vms, vmu);
5842 } else if (!strcasecmp(chan->language, "gr")){
5843 return vm_browse_messages_gr(chan, vms, vmu); /* GREEK */
5844 } else { /* Default to English syntax */
5845 return vm_browse_messages_en(chan, vms, vmu);
5849 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
5850 struct ast_vm_user *res_vmu, const char *context, const char *prefix,
5851 int skipuser, int maxlogins, int silent)
5853 int useadsi=0, valid=0, logretries=0;
5854 char password[AST_MAX_EXTENSION]="", *passptr;
5855 struct ast_vm_user vmus, *vmu = NULL;
5857 /* If ADSI is supported, setup login screen */
5858 adsi_begin(chan, &useadsi);
5859 if (!skipuser && useadsi)
5860 adsi_login(chan);
5861 if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
5862 ast_log(LOG_WARNING, "Couldn't stream login file\n");
5863 return -1;
5866 /* Authenticate them and get their mailbox/password */
5868 while (!valid && (logretries < maxlogins)) {
5869 /* Prompt for, and read in the username */
5870 if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
5871 ast_log(LOG_WARNING, "Couldn't read username\n");
5872 return -1;
5874 if (ast_strlen_zero(mailbox)) {
5875 if (chan->cid.cid_num) {
5876 ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
5877 } else {
5878 if (option_verbose > 2)
5879 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
5880 return -1;
5883 if (useadsi)
5884 adsi_password(chan);
5886 if (!ast_strlen_zero(prefix)) {
5887 char fullusername[80] = "";
5888 ast_copy_string(fullusername, prefix, sizeof(fullusername));
5889 strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
5890 ast_copy_string(mailbox, fullusername, mailbox_size);
5893 ast_log(LOG_DEBUG, "Before find user for mailbox %s\n",mailbox);
5894 vmu = find_user(&vmus, context, mailbox);
5895 if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
5896 /* saved password is blank, so don't bother asking */
5897 password[0] = '\0';
5898 } else {
5899 if (ast_streamfile(chan, "vm-password", chan->language)) {
5900 ast_log(LOG_WARNING, "Unable to stream password file\n");
5901 return -1;
5903 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
5904 ast_log(LOG_WARNING, "Unable to read password\n");
5905 return -1;
5909 if (vmu) {
5910 passptr = vmu->password;
5911 if (passptr[0] == '-') passptr++;
5913 if (vmu && !strcmp(passptr, password))
5914 valid++;
5915 else {
5916 if (option_verbose > 2)
5917 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
5918 if (!ast_strlen_zero(prefix))
5919 mailbox[0] = '\0';
5921 logretries++;
5922 if (!valid) {
5923 if (skipuser || logretries >= maxlogins) {
5924 if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
5925 ast_log(LOG_WARNING, "Unable to stream incorrect message\n");
5926 return -1;
5928 } else {
5929 if (useadsi)
5930 adsi_login(chan);
5931 if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
5932 ast_log(LOG_WARNING, "Unable to stream incorrect mailbox message\n");
5933 return -1;
5936 if (ast_waitstream(chan, "")) /* Channel is hung up */
5937 return -1;
5940 if (!valid && (logretries >= maxlogins)) {
5941 ast_stopstream(chan);
5942 ast_play_and_wait(chan, "vm-goodbye");
5943 return -1;
5945 if (vmu && !skipuser) {
5946 memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
5948 return 0;
5951 static int vm_execmain(struct ast_channel *chan, void *data)
5953 /* XXX This is, admittedly, some pretty horrendus code. For some
5954 reason it just seemed a lot easier to do with GOTO's. I feel
5955 like I'm back in my GWBASIC days. XXX */
5956 int res=-1;
5957 int cmd=0;
5958 int valid = 0;
5959 struct ast_module_user *u;
5960 char prefixstr[80] ="";
5961 char ext_context[256]="";
5962 int box;
5963 int useadsi = 0;
5964 int skipuser = 0;
5965 struct vm_state vms;
5966 struct ast_vm_user *vmu = NULL, vmus;
5967 char *context=NULL;
5968 int silentexit = 0;
5969 struct ast_flags flags = { 0 };
5970 signed char record_gain = 0;
5971 int play_auto = 0;
5972 int play_folder = 0;
5973 #ifdef IMAP_STORAGE
5974 int deleted = 0;
5975 #endif
5976 u = ast_module_user_add(chan);
5978 /* Add the vm_state to the active list and keep it active */
5979 memset(&vms, 0, sizeof(vms));
5980 vms.lastmsg = -1;
5982 memset(&vmus, 0, sizeof(vmus));
5984 if (chan->_state != AST_STATE_UP) {
5985 ast_log(LOG_DEBUG, "Before ast_answer\n");
5986 ast_answer(chan);
5989 if (!ast_strlen_zero(data)) {
5990 char *opts[OPT_ARG_ARRAY_SIZE];
5991 char *parse;
5992 AST_DECLARE_APP_ARGS(args,
5993 AST_APP_ARG(argv0);
5994 AST_APP_ARG(argv1);
5997 parse = ast_strdupa(data);
5999 AST_STANDARD_APP_ARGS(args, parse);
6001 if (args.argc == 2) {
6002 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
6003 ast_module_user_remove(u);
6004 return -1;
6006 if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
6007 int gain;
6008 if (opts[OPT_ARG_RECORDGAIN]) {
6009 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
6010 ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
6011 ast_module_user_remove(u);
6012 return -1;
6013 } else {
6014 record_gain = (signed char) gain;
6016 } else {
6017 ast_log(LOG_WARNING, "Invalid Gain level set with option g\n");
6020 if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
6021 play_auto = 1;
6022 if (opts[OPT_ARG_PLAYFOLDER]) {
6023 if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%d", &play_folder) != 1) {
6024 ast_log(LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
6026 } else {
6027 ast_log(LOG_WARNING, "Invalid folder set with option a\n");
6029 if ( play_folder > 9 || play_folder < 0) {
6030 ast_log(LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
6031 play_folder = 0;
6034 } else {
6035 /* old style options parsing */
6036 while (*(args.argv0)) {
6037 if (*(args.argv0) == 's')
6038 ast_set_flag(&flags, OPT_SILENT);
6039 else if (*(args.argv0) == 'p')
6040 ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
6041 else
6042 break;
6043 (args.argv0)++;
6048 valid = ast_test_flag(&flags, OPT_SILENT);
6050 if ((context = strchr(args.argv0, '@')))
6051 *context++ = '\0';
6053 if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
6054 ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
6055 else
6056 ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
6058 if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
6059 skipuser++;
6060 else
6061 valid = 0;
6064 if (!valid)
6065 res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
6067 ast_log(LOG_DEBUG, "After vm_authenticate\n");
6068 if (!res) {
6069 valid = 1;
6070 if (!skipuser)
6071 vmu = &vmus;
6072 } else {
6073 res = 0;
6076 /* If ADSI is supported, setup login screen */
6077 adsi_begin(chan, &useadsi);
6079 #ifdef IMAP_STORAGE
6080 vms.interactive = 1;
6081 vms.updated = 2;
6082 vmstate_insert(&vms);
6083 init_vm_state(&vms);
6084 #endif
6085 if (!valid)
6086 goto out;
6088 if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
6089 /* TODO: Handle memory allocation failure */
6091 if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
6092 /* TODO: Handle memory allocation failure */
6095 /* Set language from config to override channel language */
6096 if (!ast_strlen_zero(vmu->language))
6097 ast_string_field_set(chan, language, vmu->language);
6098 #ifndef IMAP_STORAGE
6099 create_dirpath(vms.curdir, sizeof(vms.curdir), vmu->context, vms.username, "");
6100 #endif
6101 /* Retrieve old and new message counts */
6102 ast_log(LOG_DEBUG, "Before open_mailbox\n");
6103 res = open_mailbox(&vms, vmu, 1);
6104 if (res == ERROR_LOCK_PATH)
6105 goto out;
6106 vms.oldmessages = vms.lastmsg + 1;
6107 ast_log(LOG_DEBUG, "Number of old messages: %d\n",vms.oldmessages);
6108 /* Start in INBOX */
6109 res = open_mailbox(&vms, vmu, 0);
6110 if (res == ERROR_LOCK_PATH)
6111 goto out;
6112 vms.newmessages = vms.lastmsg + 1;
6113 ast_log(LOG_DEBUG, "Number of new messages: %d\n",vms.newmessages);
6115 /* Select proper mailbox FIRST!! */
6116 if (play_auto) {
6117 res = open_mailbox(&vms, vmu, play_folder);
6118 if (res == ERROR_LOCK_PATH)
6119 goto out;
6121 /* If there are no new messages, inform the user and hangup */
6122 if (vms.lastmsg == -1) {
6123 cmd = vm_browse_messages(chan, &vms, vmu);
6124 res = 0;
6125 goto out;
6127 } else {
6128 if (!vms.newmessages && vms.oldmessages) {
6129 /* If we only have old messages start here */
6130 res = open_mailbox(&vms, vmu, 1);
6131 if (res == ERROR_LOCK_PATH)
6132 goto out;
6136 if (useadsi)
6137 adsi_status(chan, &vms);
6138 res = 0;
6140 /* Check to see if this is a new user */
6141 if (!strcasecmp(vmu->mailbox, vmu->password) &&
6142 (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
6143 if (ast_play_and_wait(chan, "vm-newuser") == -1)
6144 ast_log(LOG_WARNING, "Couldn't stream new user file\n");
6145 cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
6146 if ((cmd == 't') || (cmd == '#')) {
6147 /* Timeout */
6148 res = 0;
6149 goto out;
6150 } else if (cmd < 0) {
6151 /* Hangup */
6152 res = -1;
6153 goto out;
6156 #ifdef IMAP_STORAGE
6157 if(option_debug > 2)
6158 ast_log(LOG_DEBUG, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
6159 if (vms.quota_usage >= vms.quota_limit) {
6160 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!!\n");
6161 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
6163 #endif
6164 if (play_auto) {
6165 cmd = '1';
6166 } else {
6167 cmd = vm_intro(chan, vmu, &vms);
6170 vms.repeats = 0;
6171 vms.starting = 1;
6172 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
6173 /* Run main menu */
6174 switch (cmd) {
6175 case '1':
6176 vms.curmsg = 0;
6177 /* Fall through */
6178 case '5':
6179 cmd = vm_browse_messages(chan, &vms, vmu);
6180 break;
6181 case '2': /* Change folders */
6182 if (useadsi)
6183 adsi_folders(chan, 0, "Change to folder...");
6184 cmd = get_folder2(chan, "vm-changeto", 0);
6185 if (cmd == '#') {
6186 cmd = 0;
6187 } else if (cmd > 0) {
6188 cmd = cmd - '0';
6189 res = close_mailbox(&vms, vmu);
6190 if (res == ERROR_LOCK_PATH)
6191 goto out;
6192 res = open_mailbox(&vms, vmu, cmd);
6193 if (res == ERROR_LOCK_PATH)
6194 goto out;
6195 cmd = 0;
6197 if (useadsi)
6198 adsi_status2(chan, &vms);
6200 if (!cmd)
6201 cmd = vm_play_folder_name(chan, vms.vmbox);
6203 vms.starting = 1;
6204 break;
6205 case '3': /* Advanced options */
6206 cmd = 0;
6207 vms.repeats = 0;
6208 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
6209 switch (cmd) {
6210 case '1': /* Reply */
6211 if (vms.lastmsg > -1 && !vms.starting) {
6212 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
6213 if (cmd == ERROR_LOCK_PATH) {
6214 res = cmd;
6215 goto out;
6217 } else
6218 cmd = ast_play_and_wait(chan, "vm-sorry");
6219 cmd = 't';
6220 break;
6221 case '2': /* Callback */
6222 if (option_verbose > 2 && !vms.starting)
6223 ast_verbose( VERBOSE_PREFIX_3 "Callback Requested\n");
6224 if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
6225 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
6226 if (cmd == 9) {
6227 silentexit = 1;
6228 goto out;
6229 } else if (cmd == ERROR_LOCK_PATH) {
6230 res = cmd;
6231 goto out;
6234 else
6235 cmd = ast_play_and_wait(chan, "vm-sorry");
6236 cmd = 't';
6237 break;
6238 case '3': /* Envelope */
6239 if (vms.lastmsg > -1 && !vms.starting) {
6240 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
6241 if (cmd == ERROR_LOCK_PATH) {
6242 res = cmd;
6243 goto out;
6245 } else
6246 cmd = ast_play_and_wait(chan, "vm-sorry");
6247 cmd = 't';
6248 break;
6249 case '4': /* Dialout */
6250 if (!ast_strlen_zero(vmu->dialout)) {
6251 cmd = dialout(chan, vmu, NULL, vmu->dialout);
6252 if (cmd == 9) {
6253 silentexit = 1;
6254 goto out;
6257 else
6258 cmd = ast_play_and_wait(chan, "vm-sorry");
6259 cmd = 't';
6260 break;
6262 case '5': /* Leave VoiceMail */
6263 if (ast_test_flag(vmu, VM_SVMAIL)) {
6264 cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain);
6265 if (cmd == ERROR_LOCK_PATH) {
6266 res = cmd;
6267 goto out;
6269 } else
6270 cmd = ast_play_and_wait(chan,"vm-sorry");
6271 cmd='t';
6272 break;
6274 case '*': /* Return to main menu */
6275 cmd = 't';
6276 break;
6278 default:
6279 cmd = 0;
6280 if (!vms.starting) {
6281 cmd = ast_play_and_wait(chan, "vm-toreply");
6283 if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
6284 cmd = ast_play_and_wait(chan, "vm-tocallback");
6286 if (!cmd && !vms.starting) {
6287 cmd = ast_play_and_wait(chan, "vm-tohearenv");
6289 if (!ast_strlen_zero(vmu->dialout) && !cmd) {
6290 cmd = ast_play_and_wait(chan, "vm-tomakecall");
6292 if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
6293 cmd=ast_play_and_wait(chan, "vm-leavemsg");
6294 if (!cmd)
6295 cmd = ast_play_and_wait(chan, "vm-starmain");
6296 if (!cmd)
6297 cmd = ast_waitfordigit(chan,6000);
6298 if (!cmd)
6299 vms.repeats++;
6300 if (vms.repeats > 3)
6301 cmd = 't';
6304 if (cmd == 't') {
6305 cmd = 0;
6306 vms.repeats = 0;
6308 break;
6309 case '4':
6310 if (vms.curmsg) {
6311 vms.curmsg--;
6312 cmd = play_message(chan, vmu, &vms);
6313 } else {
6314 cmd = ast_play_and_wait(chan, "vm-nomore");
6316 break;
6317 case '6':
6318 if (vms.curmsg < vms.lastmsg) {
6319 vms.curmsg++;
6320 cmd = play_message(chan, vmu, &vms);
6321 } else {
6322 cmd = ast_play_and_wait(chan, "vm-nomore");
6324 break;
6325 case '7':
6326 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
6327 if (useadsi)
6328 adsi_delete(chan, &vms);
6329 if (vms.deleted[vms.curmsg])
6330 cmd = ast_play_and_wait(chan, "vm-deleted");
6331 else
6332 cmd = ast_play_and_wait(chan, "vm-undeleted");
6333 if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
6334 if (vms.curmsg < vms.lastmsg) {
6335 vms.curmsg++;
6336 cmd = play_message(chan, vmu, &vms);
6337 } else {
6338 cmd = ast_play_and_wait(chan, "vm-nomore");
6341 #ifdef IMAP_STORAGE
6342 deleted = 1;
6343 #endif
6344 break;
6346 case '8':
6347 if (vms.lastmsg > -1) {
6348 cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain);
6349 if (cmd == ERROR_LOCK_PATH) {
6350 res = cmd;
6351 goto out;
6353 } else
6354 cmd = ast_play_and_wait(chan, "vm-nomore");
6355 break;
6356 case '9':
6357 if (useadsi)
6358 adsi_folders(chan, 1, "Save to folder...");
6359 cmd = get_folder2(chan, "vm-savefolder", 1);
6360 box = 0; /* Shut up compiler */
6361 if (cmd == '#') {
6362 cmd = 0;
6363 break;
6364 } else if (cmd > 0) {
6365 box = cmd = cmd - '0';
6366 cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
6367 if (cmd == ERROR_LOCK_PATH) {
6368 res = cmd;
6369 goto out;
6370 #ifdef IMAP_STORAGE
6371 } else if (cmd == 10) {
6372 goto out;
6373 #endif
6374 } else if (!cmd) {
6375 vms.deleted[vms.curmsg] = 1;
6376 } else {
6377 vms.deleted[vms.curmsg] = 0;
6378 vms.heard[vms.curmsg] = 0;
6381 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
6382 if (useadsi)
6383 adsi_message(chan, &vms);
6384 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
6385 if (!cmd) {
6386 cmd = ast_play_and_wait(chan, "vm-message");
6387 if (!cmd)
6388 cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
6389 if (!cmd)
6390 cmd = ast_play_and_wait(chan, "vm-savedto");
6391 if (!cmd)
6392 cmd = vm_play_folder_name(chan, vms.fn);
6393 } else {
6394 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
6396 if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
6397 if (vms.curmsg < vms.lastmsg) {
6398 vms.curmsg++;
6399 cmd = play_message(chan, vmu, &vms);
6400 } else {
6401 cmd = ast_play_and_wait(chan, "vm-nomore");
6404 break;
6405 case '*':
6406 if (!vms.starting) {
6407 cmd = ast_play_and_wait(chan, "vm-onefor");
6408 if (!cmd)
6409 cmd = vm_play_folder_name(chan, vms.vmbox);
6410 if (!cmd)
6411 cmd = ast_play_and_wait(chan, "vm-opts");
6412 if (!cmd)
6413 cmd = vm_instructions(chan, &vms, 1);
6414 } else
6415 cmd = 0;
6416 break;
6417 case '0':
6418 cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
6419 if (useadsi)
6420 adsi_status(chan, &vms);
6421 break;
6422 default: /* Nothing */
6423 cmd = vm_instructions(chan, &vms, 0);
6424 break;
6427 if ((cmd == 't') || (cmd == '#')) {
6428 /* Timeout */
6429 res = 0;
6430 } else {
6431 /* Hangup */
6432 res = -1;
6435 out:
6436 if (res > -1) {
6437 ast_stopstream(chan);
6438 adsi_goodbye(chan);
6439 if (valid) {
6440 if (silentexit)
6441 res = ast_play_and_wait(chan, "vm-dialout");
6442 else
6443 res = ast_play_and_wait(chan, "vm-goodbye");
6444 if (res > 0)
6445 res = 0;
6447 if (useadsi)
6448 adsi_unload_session(chan);
6450 if (vmu)
6451 close_mailbox(&vms, vmu);
6452 if (valid) {
6453 snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
6454 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
6455 run_externnotify(vmu->context, vmu->mailbox);
6457 #ifdef IMAP_STORAGE
6458 /* expunge message - use UID Expunge if supported on IMAP server*/
6459 if(option_debug > 2)
6460 ast_log(LOG_DEBUG, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
6461 if (vmu && deleted == 1 && expungeonhangup == 1) {
6462 #ifdef HAVE_IMAP_TK2006
6463 if (LEVELUIDPLUS (vms.mailstream)) {
6464 mail_expunge_full(vms.mailstream,NIL,EX_UID);
6465 } else
6466 #endif
6467 mail_expunge(vms.mailstream);
6469 /* before we delete the state, we should copy pertainent info
6470 * back to the persistent model */
6471 vmstate_delete(&vms);
6472 #endif
6473 if (vmu)
6474 free_user(vmu);
6475 if (vms.deleted)
6476 free(vms.deleted);
6477 if (vms.heard)
6478 free(vms.heard);
6479 ast_module_user_remove(u);
6481 return res;
6484 static int vm_exec(struct ast_channel *chan, void *data)
6486 int res = 0;
6487 struct ast_module_user *u;
6488 char *tmp;
6489 struct leave_vm_options leave_options;
6490 struct ast_flags flags = { 0 };
6491 static int deprecate_warning = 0;
6492 char *opts[OPT_ARG_ARRAY_SIZE];
6493 AST_DECLARE_APP_ARGS(args,
6494 AST_APP_ARG(argv0);
6495 AST_APP_ARG(argv1);
6498 u = ast_module_user_add(chan);
6500 memset(&leave_options, 0, sizeof(leave_options));
6502 if (chan->_state != AST_STATE_UP)
6503 ast_answer(chan);
6505 if (!ast_strlen_zero(data)) {
6506 tmp = ast_strdupa(data);
6507 AST_STANDARD_APP_ARGS(args, tmp);
6508 if (args.argc == 2) {
6509 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
6510 ast_module_user_remove(u);
6511 return -1;
6513 ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_PRIORITY_JUMP);
6514 if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
6515 int gain;
6517 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
6518 ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
6519 ast_module_user_remove(u);
6520 return -1;
6521 } else {
6522 leave_options.record_gain = (signed char) gain;
6525 } else {
6526 /* old style options parsing */
6527 int old = 0;
6528 char *orig_argv0 = args.argv0;
6529 while (*(args.argv0)) {
6530 if (*(args.argv0) == 's') {
6531 old = 1;
6532 ast_set_flag(&leave_options, OPT_SILENT);
6533 } else if (*(args.argv0) == 'b') {
6534 old = 1;
6535 ast_set_flag(&leave_options, OPT_BUSY_GREETING);
6536 } else if (*(args.argv0) == 'u') {
6537 old = 1;
6538 ast_set_flag(&leave_options, OPT_UNAVAIL_GREETING);
6539 } else if (*(args.argv0) == 'j') {
6540 old = 1;
6541 ast_set_flag(&leave_options, OPT_PRIORITY_JUMP);
6542 } else
6543 break;
6544 (args.argv0)++;
6546 if (!deprecate_warning && old) {
6547 deprecate_warning = 1;
6548 ast_log(LOG_WARNING, "Prefixing the mailbox with an option is deprecated ('%s').\n", orig_argv0);
6549 ast_log(LOG_WARNING, "Please move all leading options to the second argument.\n");
6552 } else {
6553 char tmp[256];
6554 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
6555 if (res < 0) {
6556 ast_module_user_remove(u);
6557 return res;
6559 if (ast_strlen_zero(tmp)) {
6560 ast_module_user_remove(u);
6561 return 0;
6563 args.argv0 = ast_strdupa(tmp);
6566 res = leave_voicemail(chan, args.argv0, &leave_options);
6568 if (res == ERROR_LOCK_PATH) {
6569 ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
6570 /*Send the call to n+101 priority, where n is the current priority*/
6571 if (ast_test_flag(&leave_options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
6572 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
6573 ast_log(LOG_WARNING, "Extension %s, priority %d doesn't exist.\n", chan->exten, chan->priority + 101);
6574 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
6575 res = 0;
6578 ast_module_user_remove(u);
6580 return res;
6583 static struct ast_vm_user *find_or_create(char *context, char *mbox)
6585 struct ast_vm_user *vmu;
6586 AST_LIST_TRAVERSE(&users, vmu, list) {
6587 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mbox, vmu->mailbox))
6588 break;
6589 if (context && (!strcasecmp(context, vmu->context)) && (!strcasecmp(mbox, vmu->mailbox)))
6590 break;
6593 if (!vmu) {
6594 if ((vmu = ast_calloc(1, sizeof(*vmu)))) {
6595 ast_copy_string(vmu->context, context, sizeof(vmu->context));
6596 ast_copy_string(vmu->mailbox, mbox, sizeof(vmu->mailbox));
6597 AST_LIST_INSERT_TAIL(&users, vmu, list);
6600 return vmu;
6603 static int append_mailbox(char *context, char *mbox, char *data)
6605 /* Assumes lock is already held */
6606 char tmp[256] = "";
6607 char *stringp;
6608 char *s;
6609 struct ast_vm_user *vmu;
6611 ast_copy_string(tmp, data, sizeof(tmp));
6612 if ((vmu = find_or_create(context, mbox))) {
6613 populate_defaults(vmu);
6615 stringp = tmp;
6616 if ((s = strsep(&stringp, ",")))
6617 ast_copy_string(vmu->password, s, sizeof(vmu->password));
6618 if (stringp && (s = strsep(&stringp, ",")))
6619 ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
6620 if (stringp && (s = strsep(&stringp, ",")))
6621 ast_copy_string(vmu->email, s, sizeof(vmu->email));
6622 if (stringp && (s = strsep(&stringp, ",")))
6623 ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
6624 if (stringp && (s = strsep(&stringp, ",")))
6625 apply_options(vmu, s);
6627 return 0;
6630 static int vm_box_exists(struct ast_channel *chan, void *data)
6632 struct ast_module_user *u;
6633 struct ast_vm_user svm;
6634 char *context, *box;
6635 int priority_jump = 0;
6636 AST_DECLARE_APP_ARGS(args,
6637 AST_APP_ARG(mbox);
6638 AST_APP_ARG(options);
6641 if (ast_strlen_zero(data)) {
6642 ast_log(LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
6643 return -1;
6646 u = ast_module_user_add(chan);
6648 box = ast_strdupa(data);
6650 AST_STANDARD_APP_ARGS(args, box);
6652 if (args.options) {
6653 if (strchr(args.options, 'j'))
6654 priority_jump = 1;
6657 if ((context = strchr(args.mbox, '@'))) {
6658 *context = '\0';
6659 context++;
6662 if (find_user(&svm, context, args.mbox)) {
6663 pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
6664 if (priority_jump || ast_opt_priority_jumping)
6665 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
6666 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);
6667 } else
6668 pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
6669 ast_module_user_remove(u);
6670 return 0;
6673 static int vmauthenticate(struct ast_channel *chan, void *data)
6675 struct ast_module_user *u;
6676 char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
6677 struct ast_vm_user vmus;
6678 char *options = NULL;
6679 int silent = 0, skipuser = 0;
6680 int res = -1;
6682 u = ast_module_user_add(chan);
6684 if (s) {
6685 s = ast_strdupa(s);
6686 user = strsep(&s, "|");
6687 options = strsep(&s, "|");
6688 if (user) {
6689 s = user;
6690 user = strsep(&s, "@");
6691 context = strsep(&s, "");
6692 if (!ast_strlen_zero(user))
6693 skipuser++;
6694 ast_copy_string(mailbox, user, sizeof(mailbox));
6698 if (options) {
6699 silent = (strchr(options, 's')) != NULL;
6702 if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
6703 pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
6704 pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
6705 ast_play_and_wait(chan, "auth-thankyou");
6706 res = 0;
6709 ast_module_user_remove(u);
6710 return res;
6713 static char voicemail_show_users_help[] =
6714 "Usage: voicemail list users [for <context>]\n"
6715 " Lists all mailboxes currently set up\n";
6717 static char voicemail_show_zones_help[] =
6718 "Usage: voicemail list zones\n"
6719 " Lists zone message formats\n";
6721 static int handle_voicemail_show_users(int fd, int argc, char *argv[])
6723 struct ast_vm_user *vmu;
6724 char *output_format = "%-10s %-5s %-25s %-10s %6s\n";
6726 if ((argc < 3) || (argc > 5) || (argc == 4)) return RESULT_SHOWUSAGE;
6727 else if ((argc == 5) && strcmp(argv[3],"for")) return RESULT_SHOWUSAGE;
6729 AST_LIST_LOCK(&users);
6730 if (!AST_LIST_EMPTY(&users)) {
6731 if (argc == 3)
6732 ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
6733 else {
6734 int count = 0;
6735 AST_LIST_TRAVERSE(&users, vmu, list) {
6736 if (!strcmp(argv[4],vmu->context))
6737 count++;
6739 if (count) {
6740 ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
6741 } else {
6742 ast_cli(fd, "No such voicemail context \"%s\"\n", argv[4]);
6743 AST_LIST_UNLOCK(&users);
6744 return RESULT_FAILURE;
6747 AST_LIST_TRAVERSE(&users, vmu, list) {
6748 char dirname[256];
6749 DIR *vmdir;
6750 struct dirent *vment;
6751 int vmcount = 0;
6752 char count[12];
6754 if ((argc == 3) || ((argc == 5) && !strcmp(argv[4],vmu->context))) {
6755 make_dir(dirname, 255, vmu->context, vmu->mailbox, "INBOX");
6756 if ((vmdir = opendir(dirname))) {
6757 /* No matter what the format of VM, there will always be a .txt file for each message. */
6758 while ((vment = readdir(vmdir)))
6759 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7,".txt",4))
6760 vmcount++;
6761 closedir(vmdir);
6763 snprintf(count,sizeof(count),"%d",vmcount);
6764 ast_cli(fd, output_format, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
6767 } else {
6768 ast_cli(fd, "There are no voicemail users currently defined\n");
6769 AST_LIST_UNLOCK(&users);
6770 return RESULT_FAILURE;
6772 AST_LIST_UNLOCK(&users);
6773 return RESULT_SUCCESS;
6776 static int handle_voicemail_show_zones(int fd, int argc, char *argv[])
6778 struct vm_zone *zone;
6779 char *output_format = "%-15s %-20s %-45s\n";
6780 int res = RESULT_SUCCESS;
6782 if (argc != 3)
6783 return RESULT_SHOWUSAGE;
6785 AST_LIST_LOCK(&zones);
6786 if (!AST_LIST_EMPTY(&zones)) {
6787 ast_cli(fd, output_format, "Zone", "Timezone", "Message Format");
6788 AST_LIST_TRAVERSE(&zones, zone, list) {
6789 ast_cli(fd, output_format, zone->name, zone->timezone, zone->msg_format);
6791 } else {
6792 ast_cli(fd, "There are no voicemail zones currently defined\n");
6793 res = RESULT_FAILURE;
6795 AST_LIST_UNLOCK(&zones);
6797 return res;
6800 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
6802 int which = 0;
6803 int wordlen;
6804 struct ast_vm_user *vmu;
6805 const char *context = "";
6807 /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
6808 if (pos > 4)
6809 return NULL;
6810 if (pos == 3)
6811 return (state == 0) ? ast_strdup("for") : NULL;
6812 wordlen = strlen(word);
6813 AST_LIST_TRAVERSE(&users, vmu, list) {
6814 if (!strncasecmp(word, vmu->context, wordlen)) {
6815 if (context && strcmp(context, vmu->context) && ++which > state)
6816 return ast_strdup(vmu->context);
6817 /* ignore repeated contexts ? */
6818 context = vmu->context;
6821 return NULL;
6824 static struct ast_cli_entry cli_show_voicemail_users_deprecated = {
6825 { "show", "voicemail", "users", NULL },
6826 handle_voicemail_show_users, NULL,
6827 NULL, complete_voicemail_show_users };
6829 static struct ast_cli_entry cli_show_voicemail_zones_deprecated = {
6830 { "show", "voicemail", "zones", NULL },
6831 handle_voicemail_show_zones, NULL,
6832 NULL, NULL };
6834 static struct ast_cli_entry cli_voicemail[] = {
6835 { { "voicemail", "list", "users", NULL },
6836 handle_voicemail_show_users, "List defined voicemail boxes",
6837 voicemail_show_users_help, complete_voicemail_show_users, &cli_show_voicemail_users_deprecated },
6839 { { "voicemail", "list", "zones", NULL },
6840 handle_voicemail_show_zones, "List zone message formats",
6841 voicemail_show_zones_help, NULL, &cli_show_voicemail_zones_deprecated },
6844 static int load_config(void)
6846 struct ast_vm_user *cur;
6847 struct vm_zone *zcur;
6848 struct ast_config *cfg, *ucfg;
6849 char *cat;
6850 struct ast_variable *var;
6851 char *notifystr = NULL;
6852 char *smdistr = NULL;
6853 char *astattach;
6854 char *astsearch;
6855 char *astsaycid;
6856 char *send_voicemail;
6857 #ifdef IMAP_STORAGE
6858 char *imap_server;
6859 char *imap_port;
6860 char *imap_flags;
6861 char *auth_user;
6862 char *auth_password;
6863 char *expunge_on_hangup;
6864 #endif
6865 char *astcallop;
6866 char *astreview;
6867 char *asttempgreetwarn;
6868 char *astskipcmd;
6869 char *asthearenv;
6870 char *astsaydurationinfo;
6871 char *astsaydurationminfo;
6872 char *silencestr;
6873 char *maxmsgstr;
6874 char *astdirfwd;
6875 char *thresholdstr;
6876 char *fmt;
6877 char *astemail;
6878 char *ucontext;
6879 char *astmailcmd = SENDMAIL;
6880 char *astforcename;
6881 char *astforcegreet;
6882 char *s,*q,*stringp;
6883 char *dialoutcxt = NULL;
6884 char *callbackcxt = NULL;
6885 char *exitcxt = NULL;
6886 char *extpc;
6887 char *emaildateformatstr;
6888 char *volgainstr;
6889 int x;
6890 int tmpadsi[4];
6892 cfg = ast_config_load(VOICEMAIL_CONFIG);
6894 AST_LIST_LOCK(&users);
6895 while ((cur = AST_LIST_REMOVE_HEAD(&users, list))) {
6896 ast_set_flag(cur, VM_ALLOCED);
6897 free_user(cur);
6900 AST_LIST_LOCK(&zones);
6901 while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
6902 free_zone(zcur);
6903 AST_LIST_UNLOCK(&zones);
6905 memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
6907 if (cfg) {
6908 /* General settings */
6910 if (!(ucontext = ast_variable_retrieve(cfg, "general", "userscontext")))
6911 ucontext = "default";
6912 ast_copy_string(userscontext, ucontext, sizeof(userscontext));
6913 /* Attach voice message to mail message ? */
6914 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach")))
6915 astattach = "yes";
6916 ast_set2_flag((&globalflags), ast_true(astattach), VM_ATTACH);
6918 if (!(astsearch = ast_variable_retrieve(cfg, "general", "searchcontexts")))
6919 astsearch = "no";
6920 ast_set2_flag((&globalflags), ast_true(astsearch), VM_SEARCH);
6922 volgain = 0.0;
6923 if ((volgainstr = ast_variable_retrieve(cfg, "general", "volgain")))
6924 sscanf(volgainstr, "%lf", &volgain);
6926 #ifdef ODBC_STORAGE
6927 strcpy(odbc_database, "asterisk");
6928 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
6929 ast_copy_string(odbc_database, thresholdstr, sizeof(odbc_database));
6931 strcpy(odbc_table, "voicemessages");
6932 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbctable"))) {
6933 ast_copy_string(odbc_table, thresholdstr, sizeof(odbc_table));
6935 #endif
6936 /* Mail command */
6937 strcpy(mailcmd, SENDMAIL);
6938 if ((astmailcmd = ast_variable_retrieve(cfg, "general", "mailcmd")))
6939 ast_copy_string(mailcmd, astmailcmd, sizeof(mailcmd)); /* User setting */
6941 maxsilence = 0;
6942 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
6943 maxsilence = atoi(silencestr);
6944 if (maxsilence > 0)
6945 maxsilence *= 1000;
6948 if (!(maxmsgstr = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
6949 maxmsg = MAXMSG;
6950 } else {
6951 maxmsg = atoi(maxmsgstr);
6952 if (maxmsg <= 0) {
6953 ast_log(LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", maxmsgstr, MAXMSG);
6954 maxmsg = MAXMSG;
6955 } else if (maxmsg > MAXMSGLIMIT) {
6956 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, maxmsgstr);
6957 maxmsg = MAXMSGLIMIT;
6961 /* Load date format config for voicemail mail */
6962 if ((emaildateformatstr = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
6963 ast_copy_string(emaildateformat, emaildateformatstr, sizeof(emaildateformat));
6966 /* External password changing command */
6967 if ((extpc = ast_variable_retrieve(cfg, "general", "externpass"))) {
6968 ast_copy_string(ext_pass_cmd,extpc,sizeof(ext_pass_cmd));
6970 #ifdef IMAP_STORAGE
6971 /* IMAP server address */
6972 if ((imap_server = ast_variable_retrieve(cfg, "general", "imapserver"))) {
6973 ast_copy_string(imapserver, imap_server, sizeof(imapserver));
6974 } else {
6975 strcpy(imapserver,"localhost");
6977 /* IMAP server port */
6978 if ((imap_port = ast_variable_retrieve(cfg, "general", "imapport"))) {
6979 ast_copy_string(imapport, imap_port, sizeof(imapport));
6980 } else {
6981 strcpy(imapport,"143");
6983 /* IMAP server flags */
6984 if ((imap_flags = ast_variable_retrieve(cfg, "general", "imapflags"))) {
6985 ast_copy_string(imapflags, imap_flags, sizeof(imapflags));
6987 /* IMAP server master username */
6988 if ((auth_user = ast_variable_retrieve(cfg, "general", "authuser"))) {
6989 ast_copy_string(authuser, auth_user, sizeof(authuser));
6991 /* IMAP server master password */
6992 if ((auth_password = ast_variable_retrieve(cfg, "general", "authpassword"))) {
6993 ast_copy_string(authpassword, auth_password, sizeof(authpassword));
6995 /* Expunge on exit */
6996 if ((expunge_on_hangup = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
6997 if(!ast_false(expunge_on_hangup))
6998 expungeonhangup = 0;
6999 else
7000 expungeonhangup = 1;
7001 } else {
7002 expungeonhangup = 1;
7004 #endif
7005 /* External voicemail notify application */
7007 if ((notifystr = ast_variable_retrieve(cfg, "general", "externnotify"))) {
7008 ast_copy_string(externnotify, notifystr, sizeof(externnotify));
7009 ast_log(LOG_DEBUG, "found externnotify: %s\n", externnotify);
7010 if (!strcasecmp(externnotify, "smdi")) {
7011 ast_log(LOG_DEBUG, "Using SMDI for external voicemail notification\n");
7012 if ((smdistr = ast_variable_retrieve(cfg, "general", "smdiport"))) {
7013 smdi_iface = ast_smdi_interface_find(smdistr);
7014 } else {
7015 ast_log(LOG_DEBUG, "No SMDI interface set, trying default (/dev/ttyS0)\n");
7016 smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
7019 if (!smdi_iface) {
7020 ast_log(LOG_ERROR, "No valid SMDI interface specfied, disabling external voicemail notification\n");
7021 externnotify[0] = '\0';
7022 } else {
7023 ast_log(LOG_DEBUG, "Using SMDI port %s\n", smdi_iface->name);
7026 } else {
7027 externnotify[0] = '\0';
7030 /* Silence treshold */
7031 silencethreshold = 256;
7032 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
7033 silencethreshold = atoi(thresholdstr);
7035 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
7036 astemail = ASTERISK_USERNAME;
7037 ast_copy_string(serveremail, astemail, sizeof(serveremail));
7039 vmmaxmessage = 0;
7040 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
7041 if (sscanf(s, "%d", &x) == 1) {
7042 vmmaxmessage = x;
7043 } else {
7044 ast_log(LOG_WARNING, "Invalid max message time length\n");
7048 vmminmessage = 0;
7049 if ((s = ast_variable_retrieve(cfg, "general", "minmessage"))) {
7050 if (sscanf(s, "%d", &x) == 1) {
7051 vmminmessage = x;
7052 if (maxsilence <= vmminmessage)
7053 ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
7054 } else {
7055 ast_log(LOG_WARNING, "Invalid min message time length\n");
7058 fmt = ast_variable_retrieve(cfg, "general", "format");
7059 if (!fmt)
7060 fmt = "wav";
7061 ast_copy_string(vmfmts, fmt, sizeof(vmfmts));
7063 skipms = 3000;
7064 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
7065 if (sscanf(s, "%d", &x) == 1) {
7066 maxgreet = x;
7067 } else {
7068 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
7072 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
7073 if (sscanf(s, "%d", &x) == 1) {
7074 skipms = x;
7075 } else {
7076 ast_log(LOG_WARNING, "Invalid skipms value\n");
7080 maxlogins = 3;
7081 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
7082 if (sscanf(s, "%d", &x) == 1) {
7083 maxlogins = x;
7084 } else {
7085 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
7089 /* Force new user to record name ? */
7090 if (!(astforcename = ast_variable_retrieve(cfg, "general", "forcename")))
7091 astforcename = "no";
7092 ast_set2_flag((&globalflags), ast_true(astforcename), VM_FORCENAME);
7094 /* Force new user to record greetings ? */
7095 if (!(astforcegreet = ast_variable_retrieve(cfg, "general", "forcegreetings")))
7096 astforcegreet = "no";
7097 ast_set2_flag((&globalflags), ast_true(astforcegreet), VM_FORCEGREET);
7099 if ((s = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))){
7100 ast_log(LOG_DEBUG,"VM_CID Internal context string: %s\n",s);
7101 stringp = ast_strdupa(s);
7102 for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
7103 if (!ast_strlen_zero(stringp)) {
7104 q = strsep(&stringp,",");
7105 while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
7106 q++;
7107 ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
7108 ast_log(LOG_DEBUG,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
7109 } else {
7110 cidinternalcontexts[x][0] = '\0';
7114 if (!(astreview = ast_variable_retrieve(cfg, "general", "review"))){
7115 ast_log(LOG_DEBUG,"VM Review Option disabled globally\n");
7116 astreview = "no";
7118 ast_set2_flag((&globalflags), ast_true(astreview), VM_REVIEW);
7120 /*Temperary greeting reminder */
7121 if (!(asttempgreetwarn = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
7122 ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option disabled globally\n");
7123 asttempgreetwarn = "no";
7124 } else {
7125 ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option enabled globally\n");
7127 ast_set2_flag((&globalflags), ast_true(asttempgreetwarn), VM_TEMPGREETWARN);
7129 if (!(astcallop = ast_variable_retrieve(cfg, "general", "operator"))){
7130 ast_log(LOG_DEBUG,"VM Operator break disabled globally\n");
7131 astcallop = "no";
7133 ast_set2_flag((&globalflags), ast_true(astcallop), VM_OPERATOR);
7135 if (!(astsaycid = ast_variable_retrieve(cfg, "general", "saycid"))) {
7136 ast_log(LOG_DEBUG,"VM CID Info before msg disabled globally\n");
7137 astsaycid = "no";
7139 ast_set2_flag((&globalflags), ast_true(astsaycid), VM_SAYCID);
7141 if (!(send_voicemail = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
7142 ast_log(LOG_DEBUG,"Send Voicemail msg disabled globally\n");
7143 send_voicemail = "no";
7145 ast_set2_flag((&globalflags), ast_true(send_voicemail), VM_SVMAIL);
7147 if (!(asthearenv = ast_variable_retrieve(cfg, "general", "envelope"))) {
7148 ast_log(LOG_DEBUG,"ENVELOPE before msg enabled globally\n");
7149 asthearenv = "yes";
7151 ast_set2_flag((&globalflags), ast_true(asthearenv), VM_ENVELOPE);
7153 if (!(astsaydurationinfo = ast_variable_retrieve(cfg, "general", "sayduration"))) {
7154 ast_log(LOG_DEBUG,"Duration info before msg enabled globally\n");
7155 astsaydurationinfo = "yes";
7157 ast_set2_flag((&globalflags), ast_true(astsaydurationinfo), VM_SAYDURATION);
7159 saydurationminfo = 2;
7160 if ((astsaydurationminfo = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
7161 if (sscanf(astsaydurationminfo, "%d", &x) == 1) {
7162 saydurationminfo = x;
7163 } else {
7164 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
7168 if (!(astskipcmd = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
7169 ast_log(LOG_DEBUG,"We are not going to skip to the next msg after save/delete\n");
7170 astskipcmd = "no";
7172 ast_set2_flag((&globalflags), ast_true(astskipcmd), VM_SKIPAFTERCMD);
7174 if ((dialoutcxt = ast_variable_retrieve(cfg, "general", "dialout"))) {
7175 ast_copy_string(dialcontext, dialoutcxt, sizeof(dialcontext));
7176 ast_log(LOG_DEBUG, "found dialout context: %s\n", dialcontext);
7177 } else {
7178 dialcontext[0] = '\0';
7181 if ((callbackcxt = ast_variable_retrieve(cfg, "general", "callback"))) {
7182 ast_copy_string(callcontext, callbackcxt, sizeof(callcontext));
7183 ast_log(LOG_DEBUG, "found callback context: %s\n", callcontext);
7184 } else {
7185 callcontext[0] = '\0';
7188 if ((exitcxt = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
7189 ast_copy_string(exitcontext, exitcxt, sizeof(exitcontext));
7190 ast_log(LOG_DEBUG, "found operator context: %s\n", exitcontext);
7191 } else {
7192 exitcontext[0] = '\0';
7195 if (!(astdirfwd = ast_variable_retrieve(cfg, "general", "usedirectory")))
7196 astdirfwd = "no";
7197 ast_set2_flag((&globalflags), ast_true(astdirfwd), VM_DIRECFORWARD);
7198 if ((ucfg = ast_config_load("users.conf"))) {
7199 for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
7200 if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
7201 continue;
7202 if ((cur = find_or_create(userscontext, cat))) {
7203 populate_defaults(cur);
7204 apply_options_full(cur, ast_variable_browse(ucfg, cat));
7205 ast_copy_string(cur->context, userscontext, sizeof(cur->context));
7208 ast_config_destroy(ucfg);
7210 cat = ast_category_browse(cfg, NULL);
7211 while (cat) {
7212 if (strcasecmp(cat, "general")) {
7213 var = ast_variable_browse(cfg, cat);
7214 if (strcasecmp(cat, "zonemessages")) {
7215 /* Process mailboxes in this context */
7216 while (var) {
7217 append_mailbox(cat, var->name, var->value);
7218 var = var->next;
7220 } else {
7221 /* Timezones in this context */
7222 while (var) {
7223 struct vm_zone *z;
7224 if ((z = ast_malloc(sizeof(*z)))) {
7225 char *msg_format, *timezone;
7226 msg_format = ast_strdupa(var->value);
7227 timezone = strsep(&msg_format, "|");
7228 if (msg_format) {
7229 ast_copy_string(z->name, var->name, sizeof(z->name));
7230 ast_copy_string(z->timezone, timezone, sizeof(z->timezone));
7231 ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
7232 AST_LIST_LOCK(&zones);
7233 AST_LIST_INSERT_HEAD(&zones, z, list);
7234 AST_LIST_UNLOCK(&zones);
7235 } else {
7236 ast_log(LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
7237 free(z);
7239 } else {
7240 free(z);
7241 AST_LIST_UNLOCK(&users);
7242 ast_config_destroy(cfg);
7243 return -1;
7245 var = var->next;
7249 cat = ast_category_browse(cfg, cat);
7251 memset(fromstring,0,sizeof(fromstring));
7252 memset(pagerfromstring,0,sizeof(pagerfromstring));
7253 memset(emailtitle,0,sizeof(emailtitle));
7254 strcpy(charset, "ISO-8859-1");
7255 if (emailbody) {
7256 free(emailbody);
7257 emailbody = NULL;
7259 if (emailsubject) {
7260 free(emailsubject);
7261 emailsubject = NULL;
7263 if (pagerbody) {
7264 free(pagerbody);
7265 pagerbody = NULL;
7267 if (pagersubject) {
7268 free(pagersubject);
7269 pagersubject = NULL;
7271 if ((s = ast_variable_retrieve(cfg, "general", "pbxskip")))
7272 ast_set2_flag((&globalflags), ast_true(s), VM_PBXSKIP);
7273 if ((s = ast_variable_retrieve(cfg, "general", "fromstring")))
7274 ast_copy_string(fromstring,s,sizeof(fromstring));
7275 if ((s = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
7276 ast_copy_string(pagerfromstring,s,sizeof(pagerfromstring));
7277 if ((s = ast_variable_retrieve(cfg, "general", "charset")))
7278 ast_copy_string(charset,s,sizeof(charset));
7279 if ((s = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
7280 sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
7281 for (x = 0; x < 4; x++) {
7282 memcpy(&adsifdn[x], &tmpadsi[x], 1);
7285 if ((s = ast_variable_retrieve(cfg, "general", "adsisec"))) {
7286 sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
7287 for (x = 0; x < 4; x++) {
7288 memcpy(&adsisec[x], &tmpadsi[x], 1);
7291 if ((s = ast_variable_retrieve(cfg, "general", "adsiver")))
7292 if (atoi(s)) {
7293 adsiver = atoi(s);
7295 if ((s = ast_variable_retrieve(cfg, "general", "emailtitle"))) {
7296 ast_log(LOG_NOTICE, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
7297 ast_copy_string(emailtitle,s,sizeof(emailtitle));
7299 if ((s = ast_variable_retrieve(cfg, "general", "emailsubject")))
7300 emailsubject = ast_strdup(s);
7301 if ((s = ast_variable_retrieve(cfg, "general", "emailbody"))) {
7302 char *tmpread, *tmpwrite;
7303 emailbody = ast_strdup(s);
7305 /* substitute strings \t and \n into the apropriate characters */
7306 tmpread = tmpwrite = emailbody;
7307 while ((tmpwrite = strchr(tmpread,'\\'))) {
7308 int len = strlen("\n");
7309 switch (tmpwrite[1]) {
7310 case 'n':
7311 strncpy(tmpwrite + len, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7312 strncpy(tmpwrite, "\n", len);
7313 break;
7314 case 't':
7315 strncpy(tmpwrite + len, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7316 strncpy(tmpwrite, "\t", len);
7317 break;
7318 default:
7319 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
7321 tmpread = tmpwrite + len;
7324 if ((s = ast_variable_retrieve(cfg, "general", "pagersubject")))
7325 pagersubject = ast_strdup(s);
7326 if ((s = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
7327 char *tmpread, *tmpwrite;
7328 pagerbody = ast_strdup(s);
7330 /* substitute strings \t and \n into the apropriate characters */
7331 tmpread = tmpwrite = pagerbody;
7332 while ((tmpwrite = strchr(tmpread, '\\'))) {
7333 int len = strlen("\n");
7334 switch (tmpwrite[1]) {
7335 case 'n':
7336 strncpy(tmpwrite + len, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7337 strncpy(tmpwrite, "\n", len);
7338 break;
7339 case 't':
7340 strncpy(tmpwrite + len, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7341 strncpy(tmpwrite, "\t", len);
7342 break;
7343 default:
7344 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
7346 tmpread = tmpwrite + len;
7349 AST_LIST_UNLOCK(&users);
7350 ast_config_destroy(cfg);
7351 return 0;
7352 } else {
7353 AST_LIST_UNLOCK(&users);
7354 ast_log(LOG_WARNING, "Failed to load configuration file. Module not activated.\n");
7355 return 0;
7359 static int reload(void)
7361 return(load_config());
7364 static int unload_module(void)
7366 int res;
7368 res = ast_unregister_application(app);
7369 res |= ast_unregister_application(app2);
7370 res |= ast_unregister_application(app3);
7371 res |= ast_unregister_application(app4);
7372 ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
7373 ast_uninstall_vm_functions();
7375 ast_module_user_hangup_all();
7377 return res;
7380 static int load_module(void)
7382 int res;
7383 res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
7384 res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
7385 res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
7386 res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
7387 if (res)
7388 return(res);
7390 if ((res=load_config())) {
7391 return(res);
7394 ast_cli_register_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
7396 /* compute the location of the voicemail spool directory */
7397 snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
7399 ast_install_vm_functions(has_voicemail, inboxcount, messagecount);
7401 return res;
7404 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
7406 int cmd = 0;
7407 char destination[80] = "";
7408 int retries = 0;
7410 if (!num) {
7411 if (option_verbose > 2)
7412 ast_verbose( VERBOSE_PREFIX_3 "Destination number will be entered manually\n");
7413 while (retries < 3 && cmd != 't') {
7414 destination[1] = '\0';
7415 destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
7416 if (!cmd)
7417 destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
7418 if (!cmd)
7419 destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
7420 if (!cmd) {
7421 cmd = ast_waitfordigit(chan, 6000);
7422 if (cmd)
7423 destination[0] = cmd;
7425 if (!cmd) {
7426 retries++;
7427 } else {
7429 if (cmd < 0)
7430 return 0;
7431 if (cmd == '*') {
7432 if (option_verbose > 2)
7433 ast_verbose( VERBOSE_PREFIX_3 "User hit '*' to cancel outgoing call\n");
7434 return 0;
7436 if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0)
7437 retries++;
7438 else
7439 cmd = 't';
7442 if (retries >= 3) {
7443 return 0;
7446 } else {
7447 if (option_verbose > 2)
7448 ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
7449 ast_copy_string(destination, num, sizeof(destination));
7452 if (!ast_strlen_zero(destination)) {
7453 if (destination[strlen(destination) -1 ] == '*')
7454 return 0;
7455 if (option_verbose > 2)
7456 ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
7457 ast_copy_string(chan->exten, destination, sizeof(chan->exten));
7458 ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
7459 chan->priority = 0;
7460 return 9;
7462 return 0;
7465 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)
7467 int res = 0;
7468 #ifdef IMAP_STORAGE
7469 char origtimeS[256],cidS[256],contextS[256];
7470 char *header_content,*temp;
7471 #endif
7472 char filename[256];
7473 struct ast_config *msg_cfg;
7474 char *origtime, *cid, *context, *name, *num;
7475 int retries = 0;
7477 vms->starting = 0;
7478 #ifdef IMAP_STORAGE
7479 /* START HERE */
7480 /* get the message info!! */
7481 if(option_debug > 2)
7482 ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
7483 if (vms->msgArray[vms->curmsg] == 0) {
7484 ast_log (LOG_WARNING,"Trying to access unknown message\n");
7485 return -1;
7488 /* This will only work for new messages... */
7489 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
7490 /* empty string means no valid header */
7491 if (ast_strlen_zero(header_content)) {
7492 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
7493 return -1;
7496 /* Get info from headers!! */
7497 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:");
7499 if (temp)
7500 strcpy(cidS,temp);
7501 else
7502 cidS[0] = '\0';
7504 cid = &cidS[0];
7505 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Context:");
7507 if (temp)
7508 strcpy(contextS,temp);
7509 else
7510 contextS[0] = '\0';
7512 context = &contextS[0];
7513 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:");
7515 if (temp)
7516 strcpy(origtimeS,temp);
7517 else
7518 origtimeS[0] = '\0';
7520 origtime = &origtimeS[0];
7522 strcpy(filename, "IMAP_STORAGE");
7523 #else
7524 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
7526 /* Retrieve info from VM attribute file */
7528 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
7529 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
7530 RETRIEVE(vms->curdir, vms->curmsg);
7531 msg_cfg = ast_config_load(filename);
7532 DISPOSE(vms->curdir, vms->curmsg);
7533 if (!msg_cfg) {
7534 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
7535 return 0;
7538 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
7539 ast_config_destroy(msg_cfg);
7540 return 0;
7543 cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
7545 context = ast_variable_retrieve(msg_cfg, "message", "context");
7546 if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
7547 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
7548 #endif
7549 switch (option) {
7550 case 3:
7551 if (!res)
7552 res = play_message_datetime(chan, vmu, origtime, filename);
7553 if (!res)
7554 res = play_message_callerid(chan, vms, cid, context, 0);
7556 res = 't';
7557 break;
7559 case 2: /* Call back */
7561 if (ast_strlen_zero(cid))
7562 break;
7564 ast_callerid_parse(cid, &name, &num);
7565 while ((res > -1) && (res != 't')) {
7566 switch (res) {
7567 case '1':
7568 if (num) {
7569 /* Dial the CID number */
7570 res = dialout(chan, vmu, num, vmu->callback);
7571 if (res) {
7572 ast_config_destroy(msg_cfg);
7573 return 9;
7575 } else {
7576 res = '2';
7578 break;
7580 case '2':
7581 /* Want to enter a different number, can only do this if there's a dialout context for this user */
7582 if (!ast_strlen_zero(vmu->dialout)) {
7583 res = dialout(chan, vmu, NULL, vmu->dialout);
7584 if (res) {
7585 ast_config_destroy(msg_cfg);
7586 return 9;
7588 } else {
7589 if (option_verbose > 2)
7590 ast_verbose( VERBOSE_PREFIX_3 "Caller can not specify callback number - no dialout context available\n");
7591 res = ast_play_and_wait(chan, "vm-sorry");
7593 ast_config_destroy(msg_cfg);
7594 return res;
7595 case '*':
7596 res = 't';
7597 break;
7598 case '3':
7599 case '4':
7600 case '5':
7601 case '6':
7602 case '7':
7603 case '8':
7604 case '9':
7605 case '0':
7607 res = ast_play_and_wait(chan, "vm-sorry");
7608 retries++;
7609 break;
7610 default:
7611 if (num) {
7612 if (option_verbose > 2)
7613 ast_verbose( VERBOSE_PREFIX_3 "Confirm CID number '%s' is number to use for callback\n", num);
7614 res = ast_play_and_wait(chan, "vm-num-i-have");
7615 if (!res)
7616 res = play_message_callerid(chan, vms, num, vmu->context, 1);
7617 if (!res)
7618 res = ast_play_and_wait(chan, "vm-tocallnum");
7619 /* Only prompt for a caller-specified number if there is a dialout context specified */
7620 if (!ast_strlen_zero(vmu->dialout)) {
7621 if (!res)
7622 res = ast_play_and_wait(chan, "vm-calldiffnum");
7624 } else {
7625 res = ast_play_and_wait(chan, "vm-nonumber");
7626 if (!ast_strlen_zero(vmu->dialout)) {
7627 if (!res)
7628 res = ast_play_and_wait(chan, "vm-toenternumber");
7631 if (!res)
7632 res = ast_play_and_wait(chan, "vm-star-cancel");
7633 if (!res)
7634 res = ast_waitfordigit(chan, 6000);
7635 if (!res) {
7636 retries++;
7637 if (retries > 3)
7638 res = 't';
7640 break;
7643 if (res == 't')
7644 res = 0;
7645 else if (res == '*')
7646 res = -1;
7648 break;
7650 case 1: /* Reply */
7651 /* Send reply directly to sender */
7652 if (ast_strlen_zero(cid))
7653 break;
7655 ast_callerid_parse(cid, &name, &num);
7656 if (!num) {
7657 if (option_verbose > 2)
7658 ast_verbose(VERBOSE_PREFIX_3 "No CID number available, no reply sent\n");
7659 if (!res)
7660 res = ast_play_and_wait(chan, "vm-nonumber");
7661 ast_config_destroy(msg_cfg);
7662 return res;
7663 } else {
7664 if (find_user(NULL, vmu->context, num)) {
7665 struct leave_vm_options leave_options;
7666 char mailbox[AST_MAX_EXTENSION * 2 + 2];
7667 snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
7669 if (option_verbose > 2)
7670 ast_verbose(VERBOSE_PREFIX_3 "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
7672 memset(&leave_options, 0, sizeof(leave_options));
7673 leave_options.record_gain = record_gain;
7674 res = leave_voicemail(chan, mailbox, &leave_options);
7675 if (!res)
7676 res = 't';
7677 ast_config_destroy(msg_cfg);
7678 return res;
7679 } else {
7680 /* Sender has no mailbox, can't reply */
7681 if (option_verbose > 2)
7682 ast_verbose( VERBOSE_PREFIX_3 "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
7683 ast_play_and_wait(chan, "vm-nobox");
7684 res = 't';
7685 ast_config_destroy(msg_cfg);
7686 return res;
7689 res = 0;
7691 break;
7694 #ifndef IMAP_STORAGE
7695 ast_config_destroy(msg_cfg);
7697 if (!res) {
7698 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
7699 vms->heard[msg] = 1;
7700 res = wait_file(chan, vms, vms->fn);
7702 #endif
7703 return res;
7706 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
7707 int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
7708 signed char record_gain)
7710 /* Record message & let caller review or re-record it, or set options if applicable */
7711 int res = 0;
7712 int cmd = 0;
7713 int max_attempts = 3;
7714 int attempts = 0;
7715 int recorded = 0;
7716 int message_exists = 0;
7717 signed char zero_gain = 0;
7718 char *acceptdtmf = "#";
7719 char *canceldtmf = "";
7721 /* Note that urgent and private are for flagging messages as such in the future */
7723 /* barf if no pointer passed to store duration in */
7724 if (duration == NULL) {
7725 ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
7726 return -1;
7729 cmd = '3'; /* Want to start by recording */
7731 while ((cmd >= 0) && (cmd != 't')) {
7732 switch (cmd) {
7733 case '1':
7734 if (!message_exists) {
7735 /* In this case, 1 is to record a message */
7736 cmd = '3';
7737 break;
7738 } else {
7739 /* Otherwise 1 is to save the existing message */
7740 if (option_verbose > 2)
7741 ast_verbose(VERBOSE_PREFIX_3 "Saving message as is\n");
7742 ast_stream_and_wait(chan, "vm-msgsaved", chan->language, "");
7743 STORE(recordfile, vmu->mailbox, vmu->context, -1);
7744 DISPOSE(recordfile, -1);
7745 cmd = 't';
7746 return res;
7748 case '2':
7749 /* Review */
7750 if (option_verbose > 2)
7751 ast_verbose(VERBOSE_PREFIX_3 "Reviewing the message\n");
7752 cmd = ast_stream_and_wait(chan, recordfile, chan->language, AST_DIGIT_ANY);
7753 break;
7754 case '3':
7755 message_exists = 0;
7756 /* Record */
7757 if (recorded == 1) {
7758 if (option_verbose > 2)
7759 ast_verbose(VERBOSE_PREFIX_3 "Re-recording the message\n");
7760 } else {
7761 if (option_verbose > 2)
7762 ast_verbose(VERBOSE_PREFIX_3 "Recording the message\n");
7764 if (recorded && outsidecaller) {
7765 cmd = ast_play_and_wait(chan, INTRO);
7766 cmd = ast_play_and_wait(chan, "beep");
7768 recorded = 1;
7769 /* 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 */
7770 if (record_gain)
7771 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
7772 if (ast_test_flag(vmu, VM_OPERATOR))
7773 canceldtmf = "0";
7774 cmd = ast_play_and_record_full(chan, playfile, recordfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
7775 if (record_gain)
7776 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
7777 if (cmd == -1) {
7778 /* User has hung up, no options to give */
7779 return cmd;
7781 if (cmd == '0') {
7782 break;
7783 } else if (cmd == '*') {
7784 break;
7786 #if 0
7787 else if (vmu->review && (*duration < 5)) {
7788 /* Message is too short */
7789 if (option_verbose > 2)
7790 ast_verbose(VERBOSE_PREFIX_3 "Message too short\n");
7791 cmd = ast_play_and_wait(chan, "vm-tooshort");
7792 cmd = vm_delete(recordfile);
7793 break;
7795 else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
7796 /* Message is all silence */
7797 if (option_verbose > 2)
7798 ast_verbose(VERBOSE_PREFIX_3 "Nothing recorded\n");
7799 cmd = vm_delete(recordfile);
7800 cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
7801 if (!cmd)
7802 cmd = ast_play_and_wait(chan, "vm-speakup");
7803 break;
7805 #endif
7806 else {
7807 /* If all is well, a message exists */
7808 message_exists = 1;
7809 cmd = 0;
7811 break;
7812 case '4':
7813 case '5':
7814 case '6':
7815 case '7':
7816 case '8':
7817 case '9':
7818 case '*':
7819 case '#':
7820 cmd = ast_play_and_wait(chan, "vm-sorry");
7821 break;
7822 #if 0
7823 /* XXX Commented out for the moment because of the dangers of deleting
7824 a message while recording (can put the message numbers out of sync) */
7825 case '*':
7826 /* Cancel recording, delete message, offer to take another message*/
7827 cmd = ast_play_and_wait(chan, "vm-deleted");
7828 cmd = vm_delete(recordfile);
7829 if (outsidecaller) {
7830 res = vm_exec(chan, NULL);
7831 return res;
7833 else
7834 return 1;
7835 #endif
7836 case '0':
7837 if (!ast_test_flag(vmu, VM_OPERATOR)) {
7838 cmd = ast_play_and_wait(chan, "vm-sorry");
7839 break;
7841 if (message_exists || recorded) {
7842 cmd = ast_play_and_wait(chan, "vm-saveoper");
7843 if (!cmd)
7844 cmd = ast_waitfordigit(chan, 3000);
7845 if (cmd == '1') {
7846 ast_play_and_wait(chan, "vm-msgsaved");
7847 cmd = '0';
7848 } else {
7849 ast_play_and_wait(chan, "vm-deleted");
7850 DELETE(recordfile, -1, recordfile);
7851 cmd = '0';
7854 return cmd;
7855 default:
7856 /* If the caller is an ouside caller, and the review option is enabled,
7857 allow them to review the message, but let the owner of the box review
7858 their OGM's */
7859 if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
7860 return cmd;
7861 if (message_exists) {
7862 cmd = ast_play_and_wait(chan, "vm-review");
7864 else {
7865 cmd = ast_play_and_wait(chan, "vm-torerecord");
7866 if (!cmd)
7867 cmd = ast_waitfordigit(chan, 600);
7870 if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
7871 cmd = ast_play_and_wait(chan, "vm-reachoper");
7872 if (!cmd)
7873 cmd = ast_waitfordigit(chan, 600);
7875 #if 0
7876 if (!cmd)
7877 cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
7878 #endif
7879 if (!cmd)
7880 cmd = ast_waitfordigit(chan, 6000);
7881 if (!cmd) {
7882 attempts++;
7884 if (attempts > max_attempts) {
7885 cmd = 't';
7889 if (outsidecaller)
7890 ast_play_and_wait(chan, "vm-goodbye");
7891 if (cmd == 't')
7892 cmd = 0;
7893 return cmd;
7896 #ifdef IMAP_STORAGE
7898 static int init_mailstream(struct vm_state *vms)
7900 MAILSTREAM *stream = NIL;
7901 /* char *s; */
7902 char mbox[255];
7903 long debug;
7907 if (!vms) {
7908 ast_log (LOG_ERROR,"vm_state is NULL!\n");
7909 return -1;
7911 if(option_debug > 2)
7912 ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
7913 if (vms->mailstream == NIL || !vms->mailstream) {
7914 ast_log (LOG_DEBUG,"mailstream not set.\n");
7915 } else {
7916 stream = vms->mailstream;
7917 /* return 0; */
7919 ast_mutex_lock(&curhstusr_lock);
7920 curusr = cpystr (vms->imapuser);
7921 curhst = cpystr (mylocalhost ());
7922 /* debug = T; user wants protocol telemetry? */
7923 debug = NIL; /* NO protocol telemetry? */
7924 if (strlen(authuser) > 0) {
7925 snprintf(mbox, sizeof(mbox), "{%s:%s/imap/authuser=%s/%s/user=%s}%s",imapserver,imapport,authuser,imapflags,vms->imapuser,vms->curbox);
7926 } else {
7927 snprintf(mbox, sizeof(mbox), "{%s:%s/imap/%s/user=%s}%s",imapserver,imapport,imapflags,vms->imapuser,vms->curbox);
7929 if(option_debug > 2)
7930 ast_log (LOG_DEBUG,"Before mail_open, curusr: %s, mbox:%s\n",curusr,mbox);
7931 ast_mutex_unlock(&curhstusr_lock);
7932 vms->mailstream = mail_open (stream, mbox, debug ? OP_DEBUG : NIL);
7933 if (vms->mailstream == NIL) {
7934 return -1;
7935 } else {
7936 if (delimiter == '\0') {
7937 get_mailbox_delimiter(vms->mailstream);
7939 /* status(vms->mailstream); */
7940 return 0;
7944 static void write_file(char *filename, char *buffer, unsigned long len)
7946 FILE *output;
7948 output = fopen (filename, "w");
7949 fwrite (buffer, len, 1, output);
7950 fclose (output);
7953 void mm_searched(MAILSTREAM *stream, unsigned long number)
7955 struct vm_state *vms;
7956 char *mailbox;
7957 char *user;
7958 mailbox = stream->mailbox;
7959 user = get_user_by_mailbox(mailbox);
7961 vms = get_vm_state_by_imapuser(user,2);
7962 if (vms) {
7963 if(option_debug > 2)
7964 ast_log (LOG_DEBUG, "saving mailbox message number %lu as message %d. Interactive set to %d\n",number,vms->vmArrayIndex,vms->interactive);
7965 vms->msgArray[vms->vmArrayIndex++] = number;
7966 } else {
7967 ast_log (LOG_ERROR, "No state found.\n");
7972 /* MM display body
7973 * Accepts: BODY structure pointer
7974 * prefix string
7975 * index
7977 static void display_body(BODY *body, char *pfx, long i)
7979 char tmp[MAILTMPLEN];
7980 char *s = tmp;
7981 PARAMETER *par;
7982 PART *part; /* multipart doesn't have a row to itself */
7983 if (body->type == TYPEMULTIPART) {
7984 /* if not first time, extend prefix */
7985 if (pfx)
7986 sprintf (tmp, "%s%ld.", pfx, ++i);
7987 else
7988 tmp[0] = '\0';
7989 for (i = 0, part = body->nested.part; part; part = part->next)
7990 display_body (&part->body, tmp, i++);
7991 } else { /* non-multipart, output oneline descriptor */
7992 if (!pfx)
7993 pfx = ""; /* dummy prefix if top level */
7994 sprintf (s, " %s%ld %s", pfx, ++i, body_types[body->type]);
7995 if (body->subtype)
7996 sprintf (s += strlen (s), "/%s", body->subtype);
7997 if (body->description)
7998 sprintf (s += strlen (s), " (%s)", body->description);
7999 if ((par = body->parameter))
8001 sprintf (s += strlen (s), ";%s=%s", par->attribute, par->value);
8002 while ((par = par->next));
8003 if (body->id)
8004 sprintf (s += strlen (s), ", id = %s", body->id);
8005 switch (body->type) { /* bytes or lines depending upon body type */
8006 case TYPEMESSAGE: /* encapsulated message */
8007 case TYPETEXT: /* plain text */
8008 sprintf (s += strlen (s), " (%lu lines)", body->size.lines);
8009 break;
8010 default:
8011 sprintf (s += strlen (s), " (%lu bytes)", body->size.bytes);
8012 break;
8014 /* ast_log (LOG_NOTICE,tmp); output this line */
8015 /* encapsulated message? */
8016 if ((body->type == TYPEMESSAGE) && !strcmp (body->subtype, "RFC822") && (body = body->nested.msg->body)) {
8017 if (body->type == TYPEMULTIPART)
8018 display_body (body, pfx, i - 1);
8019 else { /* build encapsulation prefix */
8020 sprintf (tmp, "%s%ld.", pfx, i);
8021 display_body (body, tmp, (long) 0);
8027 /* MM status report
8028 * Accepts: MAIL stream
8030 static void status(MAILSTREAM *stream)
8032 unsigned long i;
8033 char *s, date[MAILTMPLEN];
8034 THREADER *thr;
8035 AUTHENTICATOR *auth;
8036 rfc822_date (date);
8037 ast_log (LOG_NOTICE,"%s\n",date);
8038 if (stream) {
8039 if (stream->mailbox)
8040 ast_log (LOG_NOTICE," %s mailbox: %s, %lu messages, %lu recent\n",
8041 stream->dtb->name, stream->mailbox, stream->nmsgs,stream->recent);
8042 else
8043 ast_log (LOG_NOTICE,"No mailbox is open on this stream\n");
8044 if (stream->user_flags[0]) {
8045 ast_log (LOG_NOTICE,"Keywords: %s\n", stream->user_flags[0]);
8046 for (i = 1; i < NUSERFLAGS && stream->user_flags[i]; ++i)
8047 ast_log (LOG_NOTICE," %s\n", stream->user_flags[i]);
8049 if (!strcmp (stream->dtb->name, "imap")) {
8050 if (LEVELIMAP4rev1 (stream))
8051 s = "IMAP4rev1 (RFC 3501)";
8052 else if (LEVEL1730 (stream))
8053 s = "IMAP4 (RFC 1730)";
8054 else if (LEVELIMAP2bis (stream))
8055 s = "IMAP2bis";
8056 else if (LEVEL1176 (stream))
8057 s = "IMAP2 (RFC 1176)";
8058 else
8059 s = "IMAP2 (RFC 1064)";
8060 ast_log (LOG_NOTICE,"%s server %s\n", s, imap_host (stream));
8061 if (LEVELIMAP4 (stream)) {
8062 if ((i = (imap_cap(stream)->auth))) {
8063 s = "";
8064 ast_log (LOG_NOTICE,"Mutually-supported SASL mechanisms:\n");
8065 while ((auth = mail_lookup_auth (find_rightmost_bit (&i) + 1))) {
8066 ast_log (LOG_NOTICE," %s\n", auth->name);
8067 if (!strcmp (auth->name, "PLAIN"))
8068 s = "\n [LOGIN will not be listed here if PLAIN is supported]\n";
8070 ast_log (LOG_NOTICE,s);
8072 ast_log (LOG_NOTICE,"Supported standard extensions:\n");
8073 if (LEVELACL (stream))
8074 ast_log (LOG_NOTICE," Access Control lists (RFC 2086)\n");
8075 if (LEVELQUOTA (stream))
8076 ast_log (LOG_NOTICE," Quotas (RFC 2087)\n");
8077 if (LEVELLITERALPLUS (stream))
8078 ast_log (LOG_NOTICE," Non-synchronizing literals (RFC 2088)\n");
8079 if (LEVELIDLE (stream))
8080 ast_log (LOG_NOTICE," IDLE unsolicited update (RFC 2177)\n");
8081 if (LEVELMBX_REF (stream))
8082 ast_log (LOG_NOTICE," Mailbox referrals (RFC 2193)\n");
8083 if (LEVELLOG_REF (stream))
8084 ast_log (LOG_NOTICE," Login referrals (RFC 2221)\n");
8085 if (LEVELANONYMOUS (stream))
8086 ast_log (LOG_NOTICE," Anonymous access (RFC 2245)\n");
8087 if (LEVELNAMESPACE (stream))
8088 ast_log (LOG_NOTICE," Multiple namespaces (RFC 2342)\n");
8089 if (LEVELUIDPLUS (stream))
8090 ast_log (LOG_NOTICE," Extended UID behavior (RFC 2359)\n");
8091 if (LEVELSTARTTLS (stream))
8092 ast_log (LOG_NOTICE," Transport Layer Security (RFC 2595)\n");
8093 if (LEVELLOGINDISABLED (stream))
8094 ast_log (LOG_NOTICE," LOGIN command disabled (RFC 2595)\n");
8095 if (LEVELID (stream))
8096 ast_log (LOG_NOTICE," Implementation identity negotiation (RFC 2971)\n");
8097 if (LEVELCHILDREN (stream))
8098 ast_log (LOG_NOTICE," LIST children announcement (RFC 3348)\n");
8099 if (LEVELMULTIAPPEND (stream))
8100 ast_log (LOG_NOTICE," Atomic multiple APPEND (RFC 3502)\n");
8101 if (LEVELBINARY (stream))
8102 ast_log (LOG_NOTICE," Binary body content (RFC 3516)\n");
8103 ast_log (LOG_NOTICE,"Supported draft extensions:\n");
8104 if (LEVELUNSELECT (stream))
8105 ast_log (LOG_NOTICE," Mailbox unselect\n");
8106 if (LEVELSASLIR (stream))
8107 ast_log (LOG_NOTICE," SASL initial client response\n");
8108 if (LEVELSORT (stream))
8109 ast_log (LOG_NOTICE," Server-based sorting\n");
8110 if (LEVELTHREAD (stream)) {
8111 ast_log (LOG_NOTICE," Server-based threading:\n");
8112 for (thr = imap_cap(stream)->threader; thr; thr = thr->next)
8113 ast_log (LOG_NOTICE," %s\n", thr->name);
8115 if (LEVELSCAN (stream))
8116 ast_log (LOG_NOTICE," Mailbox text scan\n");
8117 if ((i = imap_cap(stream)->extlevel)) {
8118 ast_log (LOG_NOTICE,"Supported BODYSTRUCTURE extensions:\n");
8119 switch (i) {
8120 case BODYEXTLOC:
8121 ast_log (LOG_NOTICE," location\n");
8122 case BODYEXTLANG:
8123 ast_log (LOG_NOTICE," language\n");
8124 case BODYEXTDSP:
8125 ast_log (LOG_NOTICE," disposition\n");
8126 case BODYEXTMD5:
8127 ast_log (LOG_NOTICE," MD5\n");
8130 }else
8131 ast_log (LOG_NOTICE,"\n");
8137 /* Interfaces to C-client */
8139 void mm_exists(MAILSTREAM * stream, unsigned long number)
8141 /* mail_ping will callback here if new mail! */
8142 if(option_debug > 3)
8143 ast_log (LOG_DEBUG, "Entering EXISTS callback for message %ld\n", number);
8144 if (number == 0) return;
8145 set_update(stream);
8149 void mm_expunged(MAILSTREAM * stream, unsigned long number)
8151 /* mail_ping will callback here if expunged mail! */
8152 if(option_debug > 3)
8153 ast_log (LOG_DEBUG, "Entering EXPUNGE callback for message %ld\n", number);
8154 if (number == 0) return;
8155 set_update(stream);
8159 void mm_flags(MAILSTREAM * stream, unsigned long number)
8161 /* mail_ping will callback here if read mail! */
8162 if(option_debug > 3)
8163 ast_log (LOG_DEBUG, "Entering FLAGS callback for message %ld\n", number);
8164 if (number == 0) return;
8165 set_update(stream);
8169 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
8171 mm_log (string, errflg);
8175 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
8177 if (delimiter)
8178 delimiter = delim;
8179 if (option_debug > 4) {
8180 ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
8181 if (attributes & LATT_NOINFERIORS)
8182 ast_log(LOG_DEBUG, "no inferiors\n");
8183 if (attributes & LATT_NOSELECT)
8184 ast_log(LOG_DEBUG, "no select\n");
8185 if (attributes & LATT_MARKED)
8186 ast_log(LOG_DEBUG, "marked\n");
8187 if (attributes & LATT_UNMARKED)
8188 ast_log(LOG_DEBUG, "unmarked\n");
8193 void mm_lsub(MAILSTREAM * stream, int delimiter, char *mailbox, long attributes)
8196 if (option_debug > 4) {
8197 ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delimiter, mailbox);
8198 if (attributes & LATT_NOINFERIORS)
8199 ast_log(LOG_DEBUG, "no inferiors\n");
8200 if (attributes & LATT_NOSELECT)
8201 ast_log(LOG_DEBUG, "no select\n");
8202 if (attributes & LATT_MARKED)
8203 ast_log(LOG_DEBUG, "marked\n");
8204 if (attributes & LATT_UNMARKED)
8205 ast_log(LOG_DEBUG, "unmarked\n");
8210 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
8212 ast_log (LOG_NOTICE," Mailbox %s", mailbox);
8213 if (status->flags & SA_MESSAGES)
8214 ast_log (LOG_NOTICE,", %lu messages", status->messages);
8215 if (status->flags & SA_RECENT)
8216 ast_log (LOG_NOTICE,", %lu recent", status->recent);
8217 if (status->flags & SA_UNSEEN)
8218 ast_log (LOG_NOTICE,", %lu unseen", status->unseen);
8219 if (status->flags & SA_UIDVALIDITY)
8220 ast_log (LOG_NOTICE,", %lu UID validity", status->uidvalidity);
8221 if (status->flags & SA_UIDNEXT)
8222 ast_log (LOG_NOTICE,", %lu next UID", status->uidnext);
8223 ast_log (LOG_NOTICE,"\n");
8227 void mm_log(char *string, long errflg)
8229 switch ((short) errflg) {
8230 case NIL:
8231 if(option_debug)
8232 ast_log(LOG_DEBUG,"IMAP Info: %s\n", string);
8233 break;
8234 case PARSE:
8235 case WARN:
8236 ast_log (LOG_WARNING,"IMAP Warning: %s\n", string);
8237 break;
8238 case ERROR:
8239 ast_log (LOG_ERROR,"IMAP Error: %s\n", string);
8240 break;
8245 void mm_dlog(char *string)
8247 ast_log (LOG_NOTICE,string);
8251 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
8253 char tmp[MAILTMPLEN];
8254 if(option_debug > 3)
8255 ast_log(LOG_DEBUG, "Entering callback mm_login\n");
8256 ast_mutex_lock(&curhstusr_lock);
8257 if (curhst)
8258 fs_give ((void **) &curhst);
8259 curhst = (char *) fs_get (1 + strlen (mb->host));
8260 strcpy (curhst, mb->host);
8261 if (*mb->user) {
8262 strcpy (user, mb->user);
8263 sprintf (tmp, "{%s/%s/user=\"%s\"} password: ", mb->host, mb->service, mb->user);
8264 } else {
8265 /* strcpy (tmp, "Password for jar: ");*/
8266 strcpy(user,curusr);
8268 if (curusr)
8269 fs_give ((void **) &curusr);
8270 ast_mutex_unlock(&curhstusr_lock);
8271 /* strcpy (pwd, getpass (tmp));*/
8272 /* We should only do this when necessary */
8273 if (strlen(authpassword) > 0) {
8274 strcpy (pwd, authpassword);
8279 void mm_critical(MAILSTREAM * stream)
8284 void mm_nocritical(MAILSTREAM * stream)
8289 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
8291 kill (getpid (), SIGSTOP);
8292 return NIL;
8296 void mm_fatal(char *string)
8298 ast_log(LOG_ERROR,"IMAP access FATAL error: %s\n", string);
8301 /* C-client callback to handle quota */
8302 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
8304 struct vm_state *vms;
8305 char *mailbox;
8306 char *user;
8307 unsigned long usage = 0;
8308 unsigned long limit = 0;
8310 while (pquota) {
8311 usage = pquota->usage;
8312 limit = pquota->limit;
8313 pquota = pquota->next;
8316 mailbox = stream->mailbox;
8317 user = get_user_by_mailbox(mailbox);
8318 vms = get_vm_state_by_imapuser(user,2);
8319 if (vms) {
8320 if(option_debug > 2)
8321 ast_log (LOG_DEBUG, "User %s usage is %lu, limit is %lu\n",user,usage,limit);
8322 vms->quota_usage = usage;
8323 vms->quota_limit = limit;
8324 } else {
8325 ast_log (LOG_ERROR, "No state found.\n");
8329 static char *get_header_by_tag(char *header, char *tag)
8331 char *start;
8332 int taglen;
8333 char *eol_pnt;
8335 if (!header || !tag)
8336 return NULL;
8338 taglen = strlen(tag) + 1;
8339 if (taglen < 1)
8340 return NULL;
8342 start = strstr(header, tag);
8343 if (!start)
8344 return NULL;
8346 ast_copy_string(temp, start+taglen, sizeof(temp));
8347 eol_pnt = strchr(temp,'\n');
8348 *eol_pnt = '\0';
8349 return temp;
8352 static char *get_user_by_mailbox(char *mailbox)
8354 char *start, *quote;
8355 char *eol_pnt;
8357 if (!mailbox)
8358 return NULL;
8360 start = strstr(mailbox,"user=");
8361 if (!start)
8362 return NULL;
8364 strcpy(temp,start+5);
8366 quote = strchr(temp,'\"');
8367 if (!quote) { /* if username is not in quotes */
8368 eol_pnt = strchr(temp,'/');
8369 if (!eol_pnt) {
8370 eol_pnt = strchr(temp,'}');
8372 *eol_pnt = '\0';
8373 return temp;
8374 } else {
8375 eol_pnt = strchr(temp+1,'\"');
8376 *eol_pnt = '\0';
8377 return temp+1;
8381 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
8383 struct vmstate *vlist = NULL;
8385 vlist = vmstates;
8386 while (vlist) {
8387 if (vlist->vms) {
8388 if (vlist->vms->imapuser) {
8389 if (!strcmp(vlist->vms->imapuser,user)) {
8390 if (interactive == 2) {
8391 return vlist->vms;
8392 } else if (vlist->vms->interactive == interactive) {
8393 return vlist->vms;
8396 } else {
8397 if(option_debug > 2)
8398 ast_log(LOG_DEBUG, " error: imapuser is NULL for %s\n",user);
8400 } else {
8401 if(option_debug > 2)
8402 ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",user);
8404 vlist = vlist->next;
8406 if(option_debug > 2)
8407 ast_log(LOG_DEBUG, "%s not found in vmstates\n",user);
8408 return NULL;
8411 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive)
8413 struct vmstate *vlist = NULL;
8415 vlist = vmstates;
8416 if(option_debug > 2)
8417 ast_log(LOG_DEBUG, "Mailbox set to %s\n",mailbox);
8418 while (vlist) {
8419 if (vlist->vms) {
8420 if (vlist->vms->username) {
8421 if(option_debug > 2)
8422 ast_log(LOG_DEBUG, " comparing mailbox %s (i=%d) to vmstate mailbox %s (i=%d)\n",mailbox,interactive,vlist->vms->username,vlist->vms->interactive);
8423 if (!strcmp(vlist->vms->username,mailbox) && vlist->vms->interactive == interactive) {
8424 if(option_debug > 2)
8425 ast_log(LOG_DEBUG, " Found it!\n");
8426 return vlist->vms;
8428 } else {
8429 if(option_debug > 2)
8430 ast_log(LOG_DEBUG, " error: username is NULL for %s\n",mailbox);
8432 } else {
8433 if(option_debug > 2)
8434 ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",mailbox);
8436 vlist = vlist->next;
8438 if(option_debug > 2)
8439 ast_log(LOG_DEBUG, "%s not found in vmstates\n",mailbox);
8440 return NULL;
8443 static void vmstate_insert(struct vm_state *vms)
8445 struct vmstate *v;
8446 struct vm_state *altvms;
8448 /* If interactive, it probably already exists, and we should
8449 use the one we already have since it is more up to date.
8450 We can compare the username to find the duplicate */
8451 if (vms->interactive == 1) {
8452 altvms = get_vm_state_by_mailbox(vms->username,0);
8453 if (altvms) {
8454 if(option_debug > 2)
8455 ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
8456 vms->newmessages = altvms->newmessages;
8457 vms->oldmessages = altvms->oldmessages;
8458 if(option_debug > 2)
8459 ast_log(LOG_DEBUG, "check_msgArray before memcpy\n");
8460 check_msgArray(vms);
8461 /* memcpy(vms->msgArray, altvms->msgArray, sizeof(long)*256); */
8462 copy_msgArray(vms, altvms);
8463 if(option_debug > 2)
8464 ast_log(LOG_DEBUG, "check_msgArray after memcpy\n");
8465 check_msgArray(vms);
8466 vms->vmArrayIndex = altvms->vmArrayIndex;
8467 vms->lastmsg = altvms->lastmsg;
8468 vms->curmsg = altvms->curmsg;
8469 /* get a pointer to the persistent store */
8470 vms->persist_vms = altvms;
8471 /* Reuse the mailstream? */
8472 /* vms->mailstream = altvms->mailstream; */
8473 vms->mailstream = NIL;
8477 v = (struct vmstate *)malloc(sizeof(struct vmstate));
8478 if (!v) {
8479 ast_log(LOG_ERROR, "Out of memory\n");
8481 if(option_debug > 2)
8482 ast_log(LOG_DEBUG, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
8483 ast_mutex_lock(&vmstate_lock);
8484 v->vms = vms;
8485 v->next = vmstates;
8486 vmstates = v;
8487 ast_mutex_unlock(&vmstate_lock);
8490 static void vmstate_delete(struct vm_state *vms)
8492 struct vmstate *vc, *vf = NULL, *vl = NULL;
8493 struct vm_state *altvms;
8495 /* If interactive, we should copy pertainent info
8496 back to the persistent state (to make update immediate) */
8497 if (vms->interactive == 1) {
8498 altvms = vms->persist_vms;
8499 if (altvms) {
8500 if(option_debug > 2)
8501 ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
8502 altvms->newmessages = vms->newmessages;
8503 altvms->oldmessages = vms->oldmessages;
8504 altvms->updated = 2;
8508 ast_mutex_lock(&vmstate_lock);
8509 vc = vmstates;
8510 if(option_debug > 2)
8511 ast_log(LOG_DEBUG, "Removing vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
8512 while (vc) {
8513 if (vc->vms == vms) {
8514 vf = vc;
8515 if (vl)
8516 vl->next = vc->next;
8517 else
8518 vmstates = vc->next;
8519 break;
8521 vl = vc;
8522 vc = vc->next;
8524 if (!vf) {
8525 ast_log(LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n",vms->imapuser,vms->username);
8526 } else {
8527 free(vf);
8529 ast_mutex_unlock(&vmstate_lock);
8532 static void set_update(MAILSTREAM * stream)
8534 struct vm_state *vms;
8535 char *mailbox;
8536 char *user;
8538 mailbox = stream->mailbox;
8539 user = get_user_by_mailbox(mailbox);
8540 vms = get_vm_state_by_imapuser(user, 0);
8541 if (vms) {
8542 if(option_debug > 2)
8543 ast_log (LOG_DEBUG, "User %s mailbox set for update.\n",user);
8544 vms->updated = 2; /* set updated flag since mailbox changed */
8545 } else {
8546 if(option_debug > 2)
8547 ast_log (LOG_WARNING, "User %s mailbox not found for update.\n",user);
8551 static void init_vm_state(struct vm_state *vms)
8553 int x = 0;
8554 vms->vmArrayIndex = 0;
8555 for (x; x<256; x++) {
8556 vms->msgArray[x] = 0;
8560 static void check_msgArray(struct vm_state *vms)
8562 int x;
8563 for (x = 0; x<256; x++) {
8564 if (vms->msgArray[x]!=0) {
8565 if(option_debug)
8566 ast_log (LOG_DEBUG, "Item %d set to %ld\n",x,vms->msgArray[x]);
8571 static void copy_msgArray(struct vm_state *dst, struct vm_state *src)
8573 int x;
8574 for (x = 0; x<256; x++) {
8575 dst->msgArray[x] = src->msgArray[x];
8579 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format)
8581 char *body_content;
8582 char *body_decoded;
8583 unsigned long len;
8584 unsigned long newlen;
8585 char filename[256];
8587 if (!body || body == NIL)
8588 return -1;
8589 display_body (body, NIL, (long) 0);
8590 body_content = mail_fetchbody (vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
8591 if (body_content != NIL) {
8592 sprintf(filename,"%s.%s", vms->fn, format);
8593 /* ast_log (LOG_DEBUG,body_content); */
8594 body_decoded = rfc822_base64 (body_content, len, &newlen);
8595 write_file (filename, (char *) body_decoded, newlen);
8597 return 0;
8600 /* get delimiter via mm_list callback */
8601 static void get_mailbox_delimiter(MAILSTREAM *stream) {
8602 mail_list(stream, "", "*");
8605 #endif /* IMAP_STORAGE */
8607 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
8608 .load = load_module,
8609 .unload = unload_module,
8610 .reload = reload,