use the proper variable type for these unixODBC API calls, eliminating warnings on...
[asterisk-bristuff.git] / apps / app_voicemail.c
blobcc063de2b7e6330c3f19a22c323297957e5db4c9
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(imaptemp_lock);
93 static char imaptemp[1024];
95 static char imapserver[48];
96 static char imapport[8];
97 static char imapflags[128];
98 static char imapfolder[64];
99 static char authuser[32];
100 static char authpassword[42];
102 static int expungeonhangup = 1;
103 AST_MUTEX_DEFINE_STATIC(delimiter_lock);
104 static char delimiter = '\0';
106 struct vm_state;
107 struct ast_vm_user;
109 static int init_mailstream (struct vm_state *vms, int box);
110 static void write_file (char *filename, char *buffer, unsigned long len);
111 /*static void status (MAILSTREAM *stream); */ /* No need for this. */
112 static void display_body (BODY *body, char *pfx, long i);
113 static char *get_header_by_tag(char *header, char *tag);
114 static void vm_imap_delete(int msgnum, struct vm_state *vms);
115 static char *get_user_by_mailbox(char *mailbox);
116 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
117 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
118 static void vmstate_insert(struct vm_state *vms);
119 static void vmstate_delete(struct vm_state *vms);
120 static void set_update(MAILSTREAM * stream);
121 static void init_vm_state(struct vm_state *vms);
122 static void check_msgArray(struct vm_state *vms);
123 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
124 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
125 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num);
126 static void get_mailbox_delimiter(MAILSTREAM *stream);
127 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
128 static void imap_mailbox_name(char *spec, struct vm_state *vms, int box, int target);
129 static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms);
130 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box);
131 struct vmstate {
132 struct vm_state *vms;
133 struct vmstate *next;
135 AST_MUTEX_DEFINE_STATIC(vmstate_lock);
136 static struct vmstate *vmstates = NULL;
137 #endif
139 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
141 #define COMMAND_TIMEOUT 5000
142 /* Don't modify these here; set your umask at runtime instead */
143 #define VOICEMAIL_DIR_MODE 0777
144 #define VOICEMAIL_FILE_MODE 0666
145 #define CHUNKSIZE 65536
147 #define VOICEMAIL_CONFIG "voicemail.conf"
148 #define ASTERISK_USERNAME "asterisk"
150 /* Default mail command to mail voicemail. Change it with the
151 mailcmd= command in voicemail.conf */
152 #define SENDMAIL "/usr/sbin/sendmail -t"
154 #define INTRO "vm-intro"
156 #define MAXMSG 100
157 #define MAXMSGLIMIT 9999
159 #define BASEMAXINLINE 256
160 #define BASELINELEN 72
161 #define BASEMAXINLINE 256
162 #define eol "\r\n"
164 #define MAX_DATETIME_FORMAT 512
165 #define MAX_NUM_CID_CONTEXTS 10
167 #define VM_REVIEW (1 << 0)
168 #define VM_OPERATOR (1 << 1)
169 #define VM_SAYCID (1 << 2)
170 #define VM_SVMAIL (1 << 3)
171 #define VM_ENVELOPE (1 << 4)
172 #define VM_SAYDURATION (1 << 5)
173 #define VM_SKIPAFTERCMD (1 << 6)
174 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
175 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
176 #define VM_PBXSKIP (1 << 9)
177 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
178 #define VM_ATTACH (1 << 11)
179 #define VM_DELETE (1 << 12)
180 #define VM_ALLOCED (1 << 13)
181 #define VM_SEARCH (1 << 14)
182 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
183 #define ERROR_LOCK_PATH -100
186 enum {
187 OPT_SILENT = (1 << 0),
188 OPT_BUSY_GREETING = (1 << 1),
189 OPT_UNAVAIL_GREETING = (1 << 2),
190 OPT_RECORDGAIN = (1 << 3),
191 OPT_PREPEND_MAILBOX = (1 << 4),
192 OPT_PRIORITY_JUMP = (1 << 5),
193 OPT_AUTOPLAY = (1 << 6),
194 } vm_option_flags;
196 enum {
197 OPT_ARG_RECORDGAIN = 0,
198 OPT_ARG_PLAYFOLDER = 1,
199 /* This *must* be the last value in this enum! */
200 OPT_ARG_ARRAY_SIZE = 2,
201 } vm_option_args;
203 AST_APP_OPTIONS(vm_app_options, {
204 AST_APP_OPTION('s', OPT_SILENT),
205 AST_APP_OPTION('b', OPT_BUSY_GREETING),
206 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
207 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
208 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
209 AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
210 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
213 static int load_config(void);
215 /*! \page vmlang Voicemail Language Syntaxes Supported
217 \par Syntaxes supported, not really language codes.
218 \arg \b en - English
219 \arg \b de - German
220 \arg \b es - Spanish
221 \arg \b fr - French
222 \arg \b it = Italian
223 \arg \b nl - Dutch
224 \arg \b pt - Polish
225 \arg \b pt - Portuguese
226 \arg \b gr - Greek
227 \arg \b no - Norwegian
228 \arg \b se - Swedish
230 German requires the following additional soundfile:
231 \arg \b 1F einE (feminine)
233 Spanish requires the following additional soundfile:
234 \arg \b 1M un (masculine)
236 Dutch, Portuguese & Spanish require the following additional soundfiles:
237 \arg \b vm-INBOXs singular of 'new'
238 \arg \b vm-Olds singular of 'old/heard/read'
240 NB these are plural:
241 \arg \b vm-INBOX nieuwe (nl)
242 \arg \b vm-Old oude (nl)
244 Polish uses:
245 \arg \b vm-new-a 'new', feminine singular accusative
246 \arg \b vm-new-e 'new', feminine plural accusative
247 \arg \b vm-new-ych 'new', feminine plural genitive
248 \arg \b vm-old-a 'old', feminine singular accusative
249 \arg \b vm-old-e 'old', feminine plural accusative
250 \arg \b vm-old-ych 'old', feminine plural genitive
251 \arg \b digits/1-a 'one', not always same as 'digits/1'
252 \arg \b digits/2-ie 'two', not always same as 'digits/2'
254 Swedish uses:
255 \arg \b vm-nytt singular of 'new'
256 \arg \b vm-nya plural of 'new'
257 \arg \b vm-gammalt singular of 'old'
258 \arg \b vm-gamla plural of 'old'
259 \arg \b digits/ett 'one', not always same as 'digits/1'
261 Norwegian uses:
262 \arg \b vm-ny singular of 'new'
263 \arg \b vm-nye plural of 'new'
264 \arg \b vm-gammel singular of 'old'
265 \arg \b vm-gamle plural of 'old'
267 Dutch also uses:
268 \arg \b nl-om 'at'?
270 Spanish also uses:
271 \arg \b vm-youhaveno
273 Italian requires the following additional soundfile:
275 For vm_intro_it:
276 \arg \b vm-nuovo new
277 \arg \b vm-nuovi new plural
278 \arg \b vm-vecchio old
279 \arg \b vm-vecchi old plural
281 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
282 spelled among others when you have to change folder. For the above reasons, vm-INBOX
283 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
287 struct baseio {
288 int iocp;
289 int iolen;
290 int linelength;
291 int ateof;
292 unsigned char iobuf[BASEMAXINLINE];
295 /*! Structure for linked list of users */
296 struct ast_vm_user {
297 char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
298 char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
299 char password[80]; /*!< Secret pin code, numbers only */
300 char fullname[80]; /*!< Full name, for directory app */
301 char email[80]; /*!< E-mail address */
302 char pager[80]; /*!< E-mail address to pager (no attachment) */
303 char serveremail[80]; /*!< From: Mail address */
304 char mailcmd[160]; /*!< Configurable mail command */
305 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
306 char zonetag[80]; /*!< Time zone */
307 char callback[80];
308 char dialout[80];
309 char uniqueid[20]; /*!< Unique integer identifier */
310 char exit[80];
311 char attachfmt[20]; /*!< Attachment format */
312 unsigned int flags; /*!< VM_ flags */
313 int saydurationm;
314 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
315 #ifdef IMAP_STORAGE
316 char imapuser[80]; /* IMAP server login */
317 char imappassword[80]; /* IMAP server password if authpassword not defined */
318 #endif
319 double volgain; /*!< Volume gain for voicemails sent via email */
320 AST_LIST_ENTRY(ast_vm_user) list;
323 struct vm_zone {
324 AST_LIST_ENTRY(vm_zone) list;
325 char name[80];
326 char timezone[80];
327 char msg_format[512];
330 struct vm_state {
331 char curbox[80];
332 char username[80];
333 char curdir[256];
334 char vmbox[256];
335 char fn[256];
336 char fn2[256];
337 int *deleted;
338 int *heard;
339 int curmsg;
340 int lastmsg;
341 int newmessages;
342 int oldmessages;
343 int starting;
344 int repeats;
345 #ifdef IMAP_STORAGE
346 int updated; /* decremented on each mail check until 1 -allows delay */
347 long msgArray[256];
348 MAILSTREAM *mailstream;
349 int vmArrayIndex;
350 char imapuser[80]; /* IMAP server login */
351 int interactive;
352 unsigned int quota_limit;
353 unsigned int quota_usage;
354 struct vm_state *persist_vms;
355 #endif
357 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);
358 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
359 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
360 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
361 signed char record_gain);
362 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
363 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
364 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
365 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap);
366 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
367 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
368 #endif
369 static void apply_options(struct ast_vm_user *vmu, const char *options);
371 #ifdef ODBC_STORAGE
372 static char odbc_database[80];
373 static char odbc_table[80];
374 #define RETRIEVE(a,b) retrieve_file(a,b)
375 #define DISPOSE(a,b) remove_file(a,b)
376 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
377 #define EXISTS(a,b,c,d) (message_exists(a,b))
378 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
379 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
380 #define DELETE(a,b,c) (delete_file(a,b))
381 #else
382 #ifdef IMAP_STORAGE
383 #define RETRIEVE(a,b)
384 #define DISPOSE(a,b)
385 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
386 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
387 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
388 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
389 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
390 #define DELETE(a,b,c) (vm_delete(c))
391 #else
392 #define RETRIEVE(a,b)
393 #define DISPOSE(a,b)
394 #define STORE(a,b,c,d,e,f,g,h,i)
395 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
396 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
397 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
398 #define DELETE(a,b,c) (vm_delete(c))
399 #endif
400 #endif
402 static char VM_SPOOL_DIR[PATH_MAX];
404 static char ext_pass_cmd[128];
406 #if ODBC_STORAGE
407 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
408 #elif IMAP_STORAGE
409 #define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
410 #else
411 #define tdesc "Comedian Mail (Voicemail System)"
412 #endif
414 static char userscontext[AST_MAX_EXTENSION] = "default";
416 static char *addesc = "Comedian Mail";
418 static char *synopsis_vm =
419 "Leave a Voicemail message";
421 static char *descrip_vm =
422 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
423 "application allows the calling party to leave a message for the specified\n"
424 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
425 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
426 "specified mailbox does not exist.\n"
427 " The Voicemail application will exit if any of the following DTMF digits are\n"
428 "received:\n"
429 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
430 " * - Jump to the 'a' extension in the current dialplan context.\n"
431 " This application will set the following channel variable upon completion:\n"
432 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
433 " application. The possible values are:\n"
434 " SUCCESS | USEREXIT | FAILED\n\n"
435 " Options:\n"
436 " b - Play the 'busy' greeting to the calling party.\n"
437 " g(#) - Use the specified amount of gain when recording the voicemail\n"
438 " message. The units are whole-number decibels (dB).\n"
439 " s - Skip the playback of instructions for leaving a message to the\n"
440 " calling party.\n"
441 " u - Play the 'unavailble greeting.\n"
442 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
443 " error occurs.\n";
445 static char *synopsis_vmain =
446 "Check Voicemail messages";
448 static char *descrip_vmain =
449 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
450 "calling party to check voicemail messages. A specific mailbox, and optional\n"
451 "corresponding context, may be specified. If a mailbox is not provided, the\n"
452 "calling party will be prompted to enter one. If a context is not specified,\n"
453 "the 'default' context will be used.\n\n"
454 " Options:\n"
455 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
456 " is entered by the caller.\n"
457 " g(#) - Use the specified amount of gain when recording a voicemail\n"
458 " message. The units are whole-number decibels (dB).\n"
459 " s - Skip checking the passcode for the mailbox.\n"
460 " a(#) - Skip folder prompt and go directly to folder specified.\n"
461 " Defaults to INBOX\n";
463 static char *synopsis_vm_box_exists =
464 "Check to see if Voicemail mailbox exists";
466 static char *descrip_vm_box_exists =
467 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
468 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
469 "will be used.\n"
470 " This application will set the following channel variable upon completion:\n"
471 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
472 " MailboxExists application. Possible values include:\n"
473 " SUCCESS | FAILED\n\n"
474 " Options:\n"
475 " j - Jump to priority n+101 if the mailbox is found.\n";
477 static char *synopsis_vmauthenticate =
478 "Authenticate with Voicemail passwords";
480 static char *descrip_vmauthenticate =
481 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
482 "same way as the Authenticate application, but the passwords are taken from\n"
483 "voicemail.conf.\n"
484 " If the mailbox is specified, only that mailbox's password will be considered\n"
485 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
486 "be set with the authenticated mailbox.\n\n"
487 " Options:\n"
488 " s - Skip playing the initial prompts.\n";
490 /* Leave a message */
491 static char *app = "VoiceMail";
493 /* Check mail, control, etc */
494 static char *app2 = "VoiceMailMain";
496 static char *app3 = "MailboxExists";
497 static char *app4 = "VMAuthenticate";
499 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
500 static AST_LIST_HEAD_STATIC(zones, vm_zone);
501 static int maxsilence;
502 static int maxmsg;
503 static int silencethreshold = 128;
504 static char serveremail[80];
505 static char mailcmd[160]; /* Configurable mail cmd */
506 static char externnotify[160];
507 static struct ast_smdi_interface *smdi_iface = NULL;
508 static char vmfmts[80];
509 static double volgain;
510 static int vmminmessage;
511 static int vmmaxmessage;
512 static int maxgreet;
513 static int skipms;
514 static int maxlogins;
516 static struct ast_flags globalflags = {0};
518 static int saydurationminfo;
520 static char dialcontext[AST_MAX_CONTEXT];
521 static char callcontext[AST_MAX_CONTEXT];
522 static char exitcontext[AST_MAX_CONTEXT];
524 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
527 static char *emailbody = NULL;
528 static char *emailsubject = NULL;
529 static char *pagerbody = NULL;
530 static char *pagersubject = NULL;
531 static char fromstring[100];
532 static char pagerfromstring[100];
533 static char emailtitle[100];
534 static char charset[32] = "ISO-8859-1";
536 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
537 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
538 static int adsiver = 1;
539 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
542 static void populate_defaults(struct ast_vm_user *vmu)
544 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
545 if (saydurationminfo)
546 vmu->saydurationm = saydurationminfo;
547 if (callcontext)
548 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
549 if (dialcontext)
550 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
551 if (exitcontext)
552 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
553 if (maxmsg)
554 vmu->maxmsg = maxmsg;
555 vmu->volgain = volgain;
558 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
560 int x;
561 if (!strcasecmp(var, "attach")) {
562 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
563 } else if (!strcasecmp(var, "attachfmt")) {
564 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
565 } else if (!strcasecmp(var, "serveremail")) {
566 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
567 } else if (!strcasecmp(var, "language")) {
568 ast_copy_string(vmu->language, value, sizeof(vmu->language));
569 } else if (!strcasecmp(var, "tz")) {
570 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
571 #ifdef IMAP_STORAGE
572 } else if (!strcasecmp(var, "imapuser")) {
573 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
574 } else if (!strcasecmp(var, "imappassword")) {
575 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
576 #endif
577 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
578 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
579 } else if (!strcasecmp(var, "saycid")){
580 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
581 } else if (!strcasecmp(var,"sendvoicemail")){
582 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
583 } else if (!strcasecmp(var, "review")){
584 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
585 } else if (!strcasecmp(var, "tempgreetwarn")){
586 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
587 } else if (!strcasecmp(var, "operator")){
588 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
589 } else if (!strcasecmp(var, "envelope")){
590 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
591 } else if (!strcasecmp(var, "sayduration")){
592 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
593 } else if (!strcasecmp(var, "saydurationm")){
594 if (sscanf(value, "%d", &x) == 1) {
595 vmu->saydurationm = x;
596 } else {
597 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
599 } else if (!strcasecmp(var, "forcename")){
600 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
601 } else if (!strcasecmp(var, "forcegreetings")){
602 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
603 } else if (!strcasecmp(var, "callback")) {
604 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
605 } else if (!strcasecmp(var, "dialout")) {
606 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
607 } else if (!strcasecmp(var, "exitcontext")) {
608 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
609 } else if (!strcasecmp(var, "maxmsg")) {
610 vmu->maxmsg = atoi(value);
611 if (vmu->maxmsg <= 0) {
612 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
613 vmu->maxmsg = MAXMSG;
614 } else if (vmu->maxmsg > MAXMSGLIMIT) {
615 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
616 vmu->maxmsg = MAXMSGLIMIT;
618 } else if (!strcasecmp(var, "volgain")) {
619 sscanf(value, "%lf", &vmu->volgain);
620 } else if (!strcasecmp(var, "options")) {
621 apply_options(vmu, value);
625 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
627 int res;
628 if (!ast_strlen_zero(vmu->uniqueid)) {
629 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
630 if (res > 0) {
631 ast_copy_string(vmu->password, password, sizeof(vmu->password));
632 res = 0;
633 } else if (!res) {
634 res = -1;
636 return res;
638 return -1;
641 static void apply_options(struct ast_vm_user *vmu, const char *options)
642 { /* Destructively Parse options and apply */
643 char *stringp;
644 char *s;
645 char *var, *value;
646 stringp = ast_strdupa(options);
647 while ((s = strsep(&stringp, "|"))) {
648 value = s;
649 if ((var = strsep(&value, "=")) && value) {
650 apply_option(vmu, var, value);
655 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
657 struct ast_variable *tmp;
658 tmp = var;
659 while (tmp) {
660 if (!strcasecmp(tmp->name, "password")) {
661 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
662 } else if (!strcasecmp(tmp->name, "uniqueid")) {
663 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
664 } else if (!strcasecmp(tmp->name, "pager")) {
665 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
666 } else if (!strcasecmp(tmp->name, "email")) {
667 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
668 } else if (!strcasecmp(tmp->name, "fullname")) {
669 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
670 } else if (!strcasecmp(tmp->name, "context")) {
671 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
672 } else
673 apply_option(retval, tmp->name, tmp->value);
674 tmp = tmp->next;
678 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
680 struct ast_variable *var;
681 struct ast_vm_user *retval;
683 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
684 if (!ivm)
685 ast_set_flag(retval, VM_ALLOCED);
686 else
687 memset(retval, 0, sizeof(*retval));
688 if (mailbox)
689 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
690 populate_defaults(retval);
691 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
692 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
693 else
694 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
695 if (var) {
696 apply_options_full(retval, var);
697 ast_variables_destroy(var);
698 } else {
699 if (!ivm)
700 free(retval);
701 retval = NULL;
704 return retval;
707 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
709 /* This function could be made to generate one from a database, too */
710 struct ast_vm_user *vmu=NULL, *cur;
711 AST_LIST_LOCK(&users);
713 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
714 context = "default";
716 AST_LIST_TRAVERSE(&users, cur, list) {
717 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
718 break;
719 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
720 break;
722 if (cur) {
723 /* Make a copy, so that on a reload, we have no race */
724 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
725 memcpy(vmu, cur, sizeof(*vmu));
726 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
727 AST_LIST_NEXT(vmu, list) = NULL;
729 } else
730 vmu = find_user_realtime(ivm, context, mailbox);
731 AST_LIST_UNLOCK(&users);
732 return vmu;
735 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
737 /* This function could be made to generate one from a database, too */
738 struct ast_vm_user *cur;
739 int res = -1;
740 AST_LIST_LOCK(&users);
741 AST_LIST_TRAVERSE(&users, cur, list) {
742 if ((!context || !strcasecmp(context, cur->context)) &&
743 (!strcasecmp(mailbox, cur->mailbox)))
744 break;
746 if (cur) {
747 ast_copy_string(cur->password, newpass, sizeof(cur->password));
748 res = 0;
750 AST_LIST_UNLOCK(&users);
751 return res;
754 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
756 /* There's probably a better way of doing this. */
757 /* That's why I've put the password change in a separate function. */
758 /* This could also be done with a database function */
760 FILE *configin;
761 FILE *configout;
762 int linenum=0;
763 char inbuf[256];
764 char orig[256];
765 char currcontext[256] ="";
766 char tmpin[PATH_MAX];
767 char tmpout[PATH_MAX];
768 struct stat statbuf;
770 if (!change_password_realtime(vmu, newpassword))
771 return;
773 snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
774 snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
775 configin = fopen(tmpin,"r");
776 if (configin)
777 configout = fopen(tmpout,"w+");
778 else
779 configout = NULL;
780 if (!configin || !configout) {
781 if (configin)
782 fclose(configin);
783 else
784 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
785 if (configout)
786 fclose(configout);
787 else
788 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
789 return;
792 while (!feof(configin)) {
793 char *user = NULL, *pass = NULL, *rest = NULL, *comment = NULL, *tmpctx = NULL, *tmpctxend = NULL;
795 /* Read in the line */
796 if (fgets(inbuf, sizeof(inbuf), configin) == NULL)
797 continue;
798 linenum++;
800 /* Make a backup of it */
801 ast_copy_string(orig, inbuf, sizeof(orig));
804 Read the file line by line, split each line into a comment and command section
805 only parse the command portion of the line
807 if (inbuf[strlen(inbuf) - 1] == '\n')
808 inbuf[strlen(inbuf) - 1] = '\0';
810 if ((comment = strchr(inbuf, ';')))
811 *comment++ = '\0'; /* Now inbuf is terminated just before the comment */
813 if (ast_strlen_zero(inbuf)) {
814 fprintf(configout, "%s", orig);
815 continue;
818 /* Check for a context, first '[' to first ']' */
819 if ((tmpctx = strchr(inbuf, '['))) {
820 tmpctxend = strchr(tmpctx, ']');
821 if (tmpctxend) {
822 /* Valid context */
823 ast_copy_string(currcontext, tmpctx + 1, tmpctxend - tmpctx);
824 fprintf(configout, "%s", orig);
825 continue;
829 /* This isn't a context line, check for MBX => PSWD... */
830 user = inbuf;
831 if ((pass = strchr(user, '='))) {
832 /* We have a line in the form of aaaaa=aaaaaa */
833 *pass++ = '\0';
835 user = ast_strip(user);
837 if (*pass == '>')
838 *pass++ = '\0';
840 pass = ast_skip_blanks(pass);
843 Since no whitespace allowed in fields, or more correctly white space
844 inside the fields is there for a purpose, we can just terminate pass
845 at the comma or EOL whichever comes first.
847 if ((rest = strchr(pass, ',')))
848 *rest++ = '\0';
849 } else {
850 user = NULL;
853 /* Compare user, pass AND context */
854 if (!ast_strlen_zero(user) && !strcmp(user, vmu->mailbox) &&
855 !ast_strlen_zero(pass) && !strcmp(pass, vmu->password) &&
856 !strcasecmp(currcontext, vmu->context)) {
857 /* This is the line */
858 if (rest) {
859 fprintf(configout, "%s => %s,%s", user, newpassword, rest);
860 } else {
861 fprintf(configout, "%s => %s", user, newpassword);
863 /* If there was a comment on the line print it out */
864 if (comment) {
865 fprintf(configout, ";%s\n", comment);
866 } else {
867 fprintf(configout, "\n");
869 } else {
870 /* Put it back like it was */
871 fprintf(configout, "%s", orig);
874 fclose(configin);
875 fclose(configout);
877 stat(tmpin, &statbuf);
878 chmod(tmpout, statbuf.st_mode);
879 chown(tmpout, statbuf.st_uid, statbuf.st_gid);
880 unlink(tmpin);
881 rename(tmpout, tmpin);
882 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
883 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
886 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
888 char buf[255];
889 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
890 if (!ast_safe_system(buf))
891 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
894 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
896 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
899 #ifdef IMAP_STORAGE
900 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num)
902 if (mkdir(dir, 01777) && (errno != EEXIST)) {
903 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
904 return sprintf(dest, "%s/msg%04d", dir, num);
906 /* return sprintf(dest, "%s/s/msg%04d", dir, imapuser, num); */
907 return sprintf(dest, "%s/msg%04d", dir, num);
910 static void vm_imap_delete(int msgnum, struct vm_state *vms)
912 unsigned long messageNum = 0;
913 char arg[10];
915 /* find real message number based on msgnum */
916 /* this may be an index into vms->msgArray based on the msgnum. */
918 messageNum = vms->msgArray[msgnum];
919 if (messageNum == 0) {
920 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
921 return;
923 if(option_debug > 2)
924 ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
925 /* delete message */
926 sprintf (arg,"%lu",messageNum);
927 mail_setflag (vms->mailstream,arg,"\\DELETED");
930 #endif
931 static int make_file(char *dest, int len, char *dir, int num)
933 return snprintf(dest, len, "%s/msg%04d", dir, num);
936 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
937 * \param dest String. base directory.
938 * \param len Length of dest.
939 * \param context String. Ignored if is null or empty string.
940 * \param ext String. Ignored if is null or empty string.
941 * \param folder String. Ignored if is null or empty string.
942 * \return 0 on failure, 1 on success.
944 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
946 mode_t mode = VOICEMAIL_DIR_MODE;
948 if (!ast_strlen_zero(context)) {
949 make_dir(dest, len, context, "", "");
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(ext)) {
956 make_dir(dest, len, context, ext, "");
957 if (mkdir(dest, mode) && errno != EEXIST) {
958 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
959 return 0;
962 if (!ast_strlen_zero(folder)) {
963 make_dir(dest, len, context, ext, folder);
964 if (mkdir(dest, mode) && errno != EEXIST) {
965 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
966 return 0;
969 return 1;
972 /* only return failure if ast_lock_path returns 'timeout',
973 not if the path does not exist or any other reason
975 static int vm_lock_path(const char *path)
977 switch (ast_lock_path(path)) {
978 case AST_LOCK_TIMEOUT:
979 return -1;
980 default:
981 return 0;
986 #ifdef ODBC_STORAGE
987 static int retrieve_file(char *dir, int msgnum)
989 int x = 0;
990 int res;
991 int fd=-1;
992 size_t fdlen = 0;
993 void *fdm=NULL;
994 SQLSMALLINT colcount=0;
995 SQLHSTMT stmt;
996 char sql[256];
997 char fmt[80]="";
998 char *c;
999 char coltitle[256];
1000 SQLSMALLINT collen;
1001 SQLSMALLINT datatype;
1002 SQLSMALLINT decimaldigits;
1003 SQLSMALLINT nullable;
1004 SQLULEN colsize;
1005 SQLLEN colsize2;
1006 FILE *f=NULL;
1007 char rowdata[80];
1008 char fn[256];
1009 char full_fn[256];
1010 char msgnums[80];
1012 struct odbc_obj *obj;
1013 obj = ast_odbc_request_obj(odbc_database, 0);
1014 if (obj) {
1015 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1016 c = strchr(fmt, '|');
1017 if (c)
1018 *c = '\0';
1019 if (!strcasecmp(fmt, "wav49"))
1020 strcpy(fmt, "WAV");
1021 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1022 if (msgnum > -1)
1023 make_file(fn, sizeof(fn), dir, msgnum);
1024 else
1025 ast_copy_string(fn, dir, sizeof(fn));
1026 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1027 f = fopen(full_fn, "w+");
1028 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1029 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1030 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1031 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1032 ast_odbc_release_obj(obj);
1033 goto yuck;
1035 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1036 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1037 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1038 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1039 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1040 ast_odbc_release_obj(obj);
1041 goto yuck;
1043 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1044 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1045 res = ast_odbc_smart_execute(obj, stmt);
1046 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1047 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1048 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1049 ast_odbc_release_obj(obj);
1050 goto yuck;
1052 res = SQLFetch(stmt);
1053 if (res == SQL_NO_DATA) {
1054 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1055 ast_odbc_release_obj(obj);
1056 goto yuck;
1058 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1059 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1060 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1061 ast_odbc_release_obj(obj);
1062 goto yuck;
1064 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, 0770);
1065 if (fd < 0) {
1066 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1067 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1068 ast_odbc_release_obj(obj);
1069 goto yuck;
1071 res = SQLNumResultCols(stmt, &colcount);
1072 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1073 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1074 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1075 ast_odbc_release_obj(obj);
1076 goto yuck;
1078 if (f)
1079 fprintf(f, "[message]\n");
1080 for (x=0;x<colcount;x++) {
1081 rowdata[0] = '\0';
1082 collen = sizeof(coltitle);
1083 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
1084 &datatype, &colsize, &decimaldigits, &nullable);
1085 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1086 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1087 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1088 ast_odbc_release_obj(obj);
1089 goto yuck;
1091 if (!strcasecmp(coltitle, "recording")) {
1092 off_t offset;
1093 res = SQLGetData(stmt, x + 1, SQL_BINARY, NULL, 0, &colsize2);
1094 fdlen = colsize2;
1095 if (fd > -1) {
1096 char tmp[1]="";
1097 lseek(fd, fdlen - 1, SEEK_SET);
1098 if (write(fd, tmp, 1) != 1) {
1099 close(fd);
1100 fd = -1;
1101 continue;
1103 /* Read out in small chunks */
1104 for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1105 /* +1 because SQLGetData likes null-terminating binary data */
1106 if ((fdm = mmap(NULL, CHUNKSIZE + 1, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == (void *)-1) {
1107 ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1108 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1109 ast_odbc_release_obj(obj);
1110 goto yuck;
1111 } else {
1112 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE + 1, NULL);
1113 munmap(fdm, 0);
1114 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1115 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1116 unlink(full_fn);
1117 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1118 ast_odbc_release_obj(obj);
1119 goto yuck;
1123 truncate(full_fn, fdlen);
1125 } else {
1126 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1127 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1128 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1129 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1130 ast_odbc_release_obj(obj);
1131 goto yuck;
1133 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1134 fprintf(f, "%s=%s\n", coltitle, rowdata);
1137 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1138 ast_odbc_release_obj(obj);
1139 } else
1140 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1141 yuck:
1142 if (f)
1143 fclose(f);
1144 if (fd > -1)
1145 close(fd);
1146 return x - 1;
1149 static int remove_file(char *dir, int msgnum)
1151 char fn[256];
1152 char full_fn[256];
1153 char msgnums[80];
1155 if (msgnum > -1) {
1156 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1157 make_file(fn, sizeof(fn), dir, msgnum);
1158 } else
1159 ast_copy_string(fn, dir, sizeof(fn));
1160 ast_filedelete(fn, NULL);
1161 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1162 unlink(full_fn);
1163 return 0;
1166 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1168 int x = 0;
1169 int res;
1170 SQLHSTMT stmt;
1171 char sql[256];
1172 char rowdata[20];
1174 struct odbc_obj *obj;
1175 obj = ast_odbc_request_obj(odbc_database, 0);
1176 if (obj) {
1177 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1178 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1179 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1180 ast_odbc_release_obj(obj);
1181 goto yuck;
1183 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1184 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1185 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1186 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1187 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1188 ast_odbc_release_obj(obj);
1189 goto yuck;
1191 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1192 res = ast_odbc_smart_execute(obj, stmt);
1193 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1194 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1195 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1196 ast_odbc_release_obj(obj);
1197 goto yuck;
1199 res = SQLFetch(stmt);
1200 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1201 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1202 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1203 ast_odbc_release_obj(obj);
1204 goto yuck;
1206 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1207 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1208 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1209 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1210 ast_odbc_release_obj(obj);
1211 goto yuck;
1213 if (sscanf(rowdata, "%d", &x) != 1)
1214 ast_log(LOG_WARNING, "Failed to read message count!\n");
1215 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1216 ast_odbc_release_obj(obj);
1217 } else
1218 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1219 yuck:
1220 return x - 1;
1223 static int message_exists(char *dir, int msgnum)
1225 int x = 0;
1226 int res;
1227 SQLHSTMT stmt;
1228 char sql[256];
1229 char rowdata[20];
1230 char msgnums[20];
1232 struct odbc_obj *obj;
1233 obj = ast_odbc_request_obj(odbc_database, 0);
1234 if (obj) {
1235 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1236 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1237 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1238 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1239 ast_odbc_release_obj(obj);
1240 goto yuck;
1242 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1243 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1244 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1245 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1246 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1247 ast_odbc_release_obj(obj);
1248 goto yuck;
1250 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1251 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1252 res = ast_odbc_smart_execute(obj, stmt);
1253 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1254 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1255 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1256 ast_odbc_release_obj(obj);
1257 goto yuck;
1259 res = SQLFetch(stmt);
1260 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1261 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1262 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1263 ast_odbc_release_obj(obj);
1264 goto yuck;
1266 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1267 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1268 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1269 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1270 ast_odbc_release_obj(obj);
1271 goto yuck;
1273 if (sscanf(rowdata, "%d", &x) != 1)
1274 ast_log(LOG_WARNING, "Failed to read message count!\n");
1275 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1276 ast_odbc_release_obj(obj);
1277 } else
1278 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1279 yuck:
1280 return x;
1283 static int count_messages(struct ast_vm_user *vmu, char *dir)
1285 return last_message_index(vmu, dir) + 1;
1288 static void delete_file(char *sdir, int smsg)
1290 int res;
1291 SQLHSTMT stmt;
1292 char sql[256];
1293 char msgnums[20];
1295 struct odbc_obj *obj;
1296 obj = ast_odbc_request_obj(odbc_database, 0);
1297 if (obj) {
1298 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1299 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1300 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1301 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1302 ast_odbc_release_obj(obj);
1303 goto yuck;
1305 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1306 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1307 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1308 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1309 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1310 ast_odbc_release_obj(obj);
1311 goto yuck;
1313 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1314 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1315 res = ast_odbc_smart_execute(obj, stmt);
1316 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1317 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1318 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1319 ast_odbc_release_obj(obj);
1320 goto yuck;
1322 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1323 ast_odbc_release_obj(obj);
1324 } else
1325 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1326 yuck:
1327 return;
1330 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1332 int res;
1333 SQLHSTMT stmt;
1334 char sql[512];
1335 char msgnums[20];
1336 char msgnumd[20];
1337 struct odbc_obj *obj;
1339 delete_file(ddir, dmsg);
1340 obj = ast_odbc_request_obj(odbc_database, 0);
1341 if (obj) {
1342 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1343 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1344 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1345 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1346 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1347 ast_odbc_release_obj(obj);
1348 goto yuck;
1350 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);
1351 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1352 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1353 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1354 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1355 ast_odbc_release_obj(obj);
1356 goto yuck;
1358 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1359 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1360 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1361 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1362 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1363 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1364 res = ast_odbc_smart_execute(obj, stmt);
1365 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1366 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1367 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1368 ast_odbc_release_obj(obj);
1369 goto yuck;
1371 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1372 ast_odbc_release_obj(obj);
1373 } else
1374 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1375 yuck:
1376 return;
1379 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1381 int x = 0;
1382 int res;
1383 int fd = -1;
1384 void *fdm=NULL;
1385 size_t fdlen = -1;
1386 SQLHSTMT stmt;
1387 SQLINTEGER len;
1388 char sql[256];
1389 char msgnums[20];
1390 char fn[256];
1391 char full_fn[256];
1392 char fmt[80]="";
1393 char *c;
1394 const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1395 const char *category = "";
1396 struct ast_config *cfg=NULL;
1397 struct odbc_obj *obj;
1399 delete_file(dir, msgnum);
1400 obj = ast_odbc_request_obj(odbc_database, 0);
1401 if (obj) {
1402 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1403 c = strchr(fmt, '|');
1404 if (c)
1405 *c = '\0';
1406 if (!strcasecmp(fmt, "wav49"))
1407 strcpy(fmt, "WAV");
1408 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1409 if (msgnum > -1)
1410 make_file(fn, sizeof(fn), dir, msgnum);
1411 else
1412 ast_copy_string(fn, dir, sizeof(fn));
1413 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1414 cfg = ast_config_load(full_fn);
1415 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1416 fd = open(full_fn, O_RDWR);
1417 if (fd < 0) {
1418 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1419 ast_odbc_release_obj(obj);
1420 goto yuck;
1422 if (cfg) {
1423 context = ast_variable_retrieve(cfg, "message", "context");
1424 if (!context) context = "";
1425 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1426 if (!macrocontext) macrocontext = "";
1427 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1428 if (!callerid) callerid = "";
1429 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1430 if (!origtime) origtime = "";
1431 duration = ast_variable_retrieve(cfg, "message", "duration");
1432 if (!duration) duration = "";
1433 category = ast_variable_retrieve(cfg, "message", "category");
1434 if (!category) category = "";
1436 fdlen = lseek(fd, 0, SEEK_END);
1437 lseek(fd, 0, SEEK_SET);
1438 printf("Length is %zd\n", fdlen);
1439 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1440 if (!fdm) {
1441 ast_log(LOG_WARNING, "Memory map failed!\n");
1442 ast_odbc_release_obj(obj);
1443 goto yuck;
1445 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1446 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1447 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1448 ast_odbc_release_obj(obj);
1449 goto yuck;
1451 if (!ast_strlen_zero(category))
1452 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1453 else
1454 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1455 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1456 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1457 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1458 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1459 ast_odbc_release_obj(obj);
1460 goto yuck;
1462 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1463 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1464 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1465 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1466 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1467 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1468 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1469 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1470 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1471 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1472 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1473 if (!ast_strlen_zero(category))
1474 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1475 res = ast_odbc_smart_execute(obj, stmt);
1476 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1477 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1478 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1479 ast_odbc_release_obj(obj);
1480 goto yuck;
1482 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1483 ast_odbc_release_obj(obj);
1484 } else
1485 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1486 yuck:
1487 if (cfg)
1488 ast_config_destroy(cfg);
1489 if (fdm)
1490 munmap(fdm, fdlen);
1491 if (fd > -1)
1492 close(fd);
1493 return x;
1496 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1498 int res;
1499 SQLHSTMT stmt;
1500 char sql[256];
1501 char msgnums[20];
1502 char msgnumd[20];
1503 struct odbc_obj *obj;
1505 delete_file(ddir, dmsg);
1506 obj = ast_odbc_request_obj(odbc_database, 0);
1507 if (obj) {
1508 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1509 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1510 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1511 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1512 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1513 ast_odbc_release_obj(obj);
1514 goto yuck;
1516 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1517 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1518 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1519 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1520 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1521 ast_odbc_release_obj(obj);
1522 goto yuck;
1524 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1525 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1526 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1527 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1528 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1529 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1530 res = ast_odbc_smart_execute(obj, stmt);
1531 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1532 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1533 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1534 ast_odbc_release_obj(obj);
1535 goto yuck;
1537 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1538 ast_odbc_release_obj(obj);
1539 } else
1540 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1541 yuck:
1542 return;
1545 #else
1546 #ifndef IMAP_STORAGE
1547 static int count_messages(struct ast_vm_user *vmu, char *dir)
1549 /* Find all .txt files - even if they are not in sequence from 0000 */
1551 int vmcount = 0;
1552 DIR *vmdir = NULL;
1553 struct dirent *vment = NULL;
1555 if (vm_lock_path(dir))
1556 return ERROR_LOCK_PATH;
1558 if ((vmdir = opendir(dir))) {
1559 while ((vment = readdir(vmdir))) {
1560 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1561 vmcount++;
1563 closedir(vmdir);
1565 ast_unlock_path(dir);
1567 return vmcount;
1570 static void rename_file(char *sfn, char *dfn)
1572 char stxt[256];
1573 char dtxt[256];
1574 ast_filerename(sfn,dfn,NULL);
1575 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1576 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1577 rename(stxt, dtxt);
1580 static int copy(char *infile, char *outfile)
1582 int ifd;
1583 int ofd;
1584 int res;
1585 int len;
1586 char buf[4096];
1588 #ifdef HARDLINK_WHEN_POSSIBLE
1589 /* Hard link if possible; saves disk space & is faster */
1590 if (link(infile, outfile)) {
1591 #endif
1592 if ((ifd = open(infile, O_RDONLY)) < 0) {
1593 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1594 return -1;
1596 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1597 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1598 close(ifd);
1599 return -1;
1601 do {
1602 len = read(ifd, buf, sizeof(buf));
1603 if (len < 0) {
1604 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1605 close(ifd);
1606 close(ofd);
1607 unlink(outfile);
1609 if (len) {
1610 res = write(ofd, buf, len);
1611 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1612 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1613 close(ifd);
1614 close(ofd);
1615 unlink(outfile);
1618 } while (len);
1619 close(ifd);
1620 close(ofd);
1621 return 0;
1622 #ifdef HARDLINK_WHEN_POSSIBLE
1623 } else {
1624 /* Hard link succeeded */
1625 return 0;
1627 #endif
1630 static void copy_file(char *frompath, char *topath)
1632 char frompath2[256],topath2[256];
1633 ast_filecopy(frompath, topath, NULL);
1634 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1635 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1636 copy(frompath2, topath2);
1638 #endif
1640 * A negative return value indicates an error.
1642 #if (!defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
1643 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1645 int x;
1646 char fn[256];
1648 if (vm_lock_path(dir))
1649 return ERROR_LOCK_PATH;
1651 for (x = 0; x < vmu->maxmsg; x++) {
1652 make_file(fn, sizeof(fn), dir, x);
1653 if (ast_fileexists(fn, NULL, NULL) < 1)
1654 break;
1656 ast_unlock_path(dir);
1658 return x - 1;
1660 #endif
1662 static int vm_delete(char *file)
1664 char *txt;
1665 int txtsize = 0;
1667 txtsize = (strlen(file) + 5)*sizeof(char);
1668 txt = alloca(txtsize);
1669 /* Sprintf here would safe because we alloca'd exactly the right length,
1670 * but trying to eliminate all sprintf's anyhow
1672 snprintf(txt, txtsize, "%s.txt", file);
1673 unlink(txt);
1674 return ast_filedelete(file, NULL);
1678 #endif
1679 static int inbuf(struct baseio *bio, FILE *fi)
1681 int l;
1683 if (bio->ateof)
1684 return 0;
1686 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1687 if (ferror(fi))
1688 return -1;
1690 bio->ateof = 1;
1691 return 0;
1694 bio->iolen= l;
1695 bio->iocp= 0;
1697 return 1;
1700 static int inchar(struct baseio *bio, FILE *fi)
1702 if (bio->iocp>=bio->iolen) {
1703 if (!inbuf(bio, fi))
1704 return EOF;
1707 return bio->iobuf[bio->iocp++];
1710 static int ochar(struct baseio *bio, int c, FILE *so)
1712 if (bio->linelength>=BASELINELEN) {
1713 if (fputs(eol,so)==EOF)
1714 return -1;
1716 bio->linelength= 0;
1719 if (putc(((unsigned char)c),so)==EOF)
1720 return -1;
1722 bio->linelength++;
1724 return 1;
1727 static int base_encode(char *filename, FILE *so)
1729 unsigned char dtable[BASEMAXINLINE];
1730 int i,hiteof= 0;
1731 FILE *fi;
1732 struct baseio bio;
1734 memset(&bio, 0, sizeof(bio));
1735 bio.iocp = BASEMAXINLINE;
1737 if (!(fi = fopen(filename, "rb"))) {
1738 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1739 return -1;
1742 for (i= 0;i<9;i++) {
1743 dtable[i]= 'A'+i;
1744 dtable[i+9]= 'J'+i;
1745 dtable[26+i]= 'a'+i;
1746 dtable[26+i+9]= 'j'+i;
1748 for (i= 0;i<8;i++) {
1749 dtable[i+18]= 'S'+i;
1750 dtable[26+i+18]= 's'+i;
1752 for (i= 0;i<10;i++) {
1753 dtable[52+i]= '0'+i;
1755 dtable[62]= '+';
1756 dtable[63]= '/';
1758 while (!hiteof){
1759 unsigned char igroup[3],ogroup[4];
1760 int c,n;
1762 igroup[0]= igroup[1]= igroup[2]= 0;
1764 for (n= 0;n<3;n++) {
1765 if ((c = inchar(&bio, fi)) == EOF) {
1766 hiteof= 1;
1767 break;
1770 igroup[n]= (unsigned char)c;
1773 if (n> 0) {
1774 ogroup[0]= dtable[igroup[0]>>2];
1775 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1776 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1777 ogroup[3]= dtable[igroup[2]&0x3F];
1779 if (n<3) {
1780 ogroup[3]= '=';
1782 if (n<2)
1783 ogroup[2]= '=';
1786 for (i= 0;i<4;i++)
1787 ochar(&bio, ogroup[i], so);
1791 if (fputs(eol,so)==EOF)
1792 return 0;
1794 fclose(fi);
1796 return 1;
1799 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)
1801 char callerid[256];
1802 /* Prepare variables for substition in email body and subject */
1803 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1804 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1805 snprintf(passdata, passdatasize, "%d", msgnum);
1806 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1807 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1808 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1809 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1810 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1811 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1812 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1813 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1817 * fill in *tm for current time according to the proper timezone, if any.
1818 * Return tm so it can be used as a function argument.
1820 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1822 const struct vm_zone *z = NULL;
1823 time_t t = time(NULL);
1825 /* Does this user have a timezone specified? */
1826 if (!ast_strlen_zero(vmu->zonetag)) {
1827 /* Find the zone in the list */
1828 AST_LIST_LOCK(&zones);
1829 AST_LIST_TRAVERSE(&zones, z, list) {
1830 if (!strcmp(z->name, vmu->zonetag))
1831 break;
1833 AST_LIST_UNLOCK(&zones);
1835 ast_localtime(&t, tm, z ? z->timezone : NULL);
1836 return tm;
1839 /* same as mkstemp, but return a FILE * */
1840 static FILE *vm_mkftemp(char *template)
1842 FILE *p = NULL;
1843 int pfd = mkstemp(template);
1844 if (pfd > -1) {
1845 p = fdopen(pfd, "w+");
1846 if (!p) {
1847 close(pfd);
1848 pfd = -1;
1851 return p;
1854 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap)
1856 char date[256];
1857 char host[MAXHOSTNAMELEN] = "";
1858 char who[256];
1859 char bound[256];
1860 char fname[256];
1861 char dur[256];
1862 char tmpcmd[256];
1863 struct tm tm;
1865 gethostname(host, sizeof(host) - 1);
1866 if (strchr(srcemail, '@'))
1867 ast_copy_string(who, srcemail, sizeof(who));
1868 else {
1869 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1871 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1872 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1873 fprintf(p, "Date: %s\r\n", date);
1875 /* Set date format for voicemail mail */
1876 strftime(date, sizeof(date), emaildateformat, &tm);
1878 if (*fromstring) {
1879 struct ast_channel *ast;
1880 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1881 char *passdata;
1882 int vmlen = strlen(fromstring)*3 + 200;
1883 if ((passdata = alloca(vmlen))) {
1884 memset(passdata, 0, vmlen);
1885 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1886 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1887 fprintf(p, "From: %s <%s>\r\n", passdata, who);
1888 } else
1889 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1890 ast_channel_free(ast);
1891 } else
1892 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1893 } else
1894 fprintf(p, "From: Asterisk PBX <%s>\r\n", who);
1895 fprintf(p, "To: %s <%s>\r\n", vmu->fullname, vmu->email);
1896 if (emailsubject) {
1897 struct ast_channel *ast;
1898 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1899 char *passdata;
1900 int vmlen = strlen(emailsubject)*3 + 200;
1901 if ((passdata = alloca(vmlen))) {
1902 memset(passdata, 0, vmlen);
1903 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1904 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
1905 fprintf(p, "Subject: %s\r\n", passdata);
1906 } else
1907 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1908 ast_channel_free(ast);
1909 } else
1910 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1911 } else if (*emailtitle) {
1912 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1913 fprintf(p,"\r\n") ;
1914 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1915 fprintf(p, "Subject: New message %d in mailbox %s\r\n", msgnum + 1, mailbox);
1916 else
1917 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\r\n", msgnum + 1, mailbox);
1918 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\r\n", msgnum, (unsigned int)ast_random(), mailbox, getpid(), host);
1919 if(imap) {
1920 /* additional information needed for IMAP searching */
1921 fprintf(p, "X-Asterisk-VM-Message-Num: %d\r\n", msgnum + 1);
1922 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s\r\n", ext); */
1923 fprintf(p, "X-Asterisk-VM-Server-Name: %s\r\n", fromstring);
1924 fprintf(p, "X-Asterisk-VM-Context: %s\r\n", context);
1925 fprintf(p, "X-Asterisk-VM-Extension: %s\r\n", mailbox);
1926 fprintf(p, "X-Asterisk-VM-Priority: %d\r\n", chan->priority);
1927 fprintf(p, "X-Asterisk-VM-Caller-channel: %s\r\n", chan->name);
1928 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s\r\n", cidnum);
1929 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s\r\n", cidname);
1930 fprintf(p, "X-Asterisk-VM-Duration: %d\r\n", duration);
1931 if (!ast_strlen_zero(category))
1932 fprintf(p, "X-Asterisk-VM-Category: %s\r\n", category);
1933 fprintf(p, "X-Asterisk-VM-Orig-date: %s\r\n", date);
1934 fprintf(p, "X-Asterisk-VM-Orig-time: %ld\r\n", (long)time(NULL));
1936 if (!ast_strlen_zero(cidnum))
1937 fprintf(p, "X-Asterisk-CallerID: %s\r\n", cidnum);
1938 if (!ast_strlen_zero(cidname))
1939 fprintf(p, "X-Asterisk-CallerIDName: %s\r\n", cidname);
1940 fprintf(p, "MIME-Version: 1.0\r\n");
1941 if (attach_user_voicemail) {
1942 /* Something unique. */
1943 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)ast_random());
1945 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\r\n\r\n\r\n", bound);
1947 fprintf(p, "--%s\r\n", bound);
1949 fprintf(p, "Content-Type: text/plain; charset=%s\r\nContent-Transfer-Encoding: 8bit\r\n\r\n", charset);
1950 if (emailbody) {
1951 struct ast_channel *ast;
1952 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1953 char *passdata;
1954 int vmlen = strlen(emailbody)*3 + 200;
1955 if ((passdata = alloca(vmlen))) {
1956 memset(passdata, 0, vmlen);
1957 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1958 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
1959 fprintf(p, "%s\r\n", passdata);
1960 } else
1961 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1962 ast_channel_free(ast);
1963 } else
1964 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1965 } else {
1966 fprintf(p, "Dear %s:\r\n\r\n\tJust wanted to let you know you were just left a %s long message (number %d)\r\n"
1968 "in mailbox %s from %s, on %s so you might\r\n"
1969 "want to check it when you get a chance. Thanks!\r\n\r\n\t\t\t\t--Asterisk\r\n\r\n", vmu->fullname,
1970 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1972 if (attach_user_voicemail) {
1973 /* Eww. We want formats to tell us their own MIME type */
1974 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
1975 char tmpdir[256], newtmp[256];
1976 int tmpfd;
1978 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
1979 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
1980 tmpfd = mkstemp(newtmp);
1981 ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
1982 if (vmu->volgain < -.001 || vmu->volgain > .001) {
1983 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
1984 ast_safe_system(tmpcmd);
1985 attach = newtmp;
1986 ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
1988 fprintf(p, "--%s\r\n", bound);
1989 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\r\n", ctype, format, msgnum, format);
1990 fprintf(p, "Content-Transfer-Encoding: base64\r\n");
1991 fprintf(p, "Content-Description: Voicemail sound attachment.\r\n");
1992 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\r\n\r\n", msgnum, format);
1993 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1994 base_encode(fname, p);
1995 /* only attach if necessary */
1996 if (imap && strcmp(format, "gsm")) {
1997 fprintf(p, "--%s\r\n", bound);
1998 fprintf(p, "Content-Type: audio/x-gsm; name=\"msg%04d.%s\"\r\n", msgnum, format);
1999 fprintf(p, "Content-Transfer-Encoding: base64\r\n");
2000 fprintf(p, "Content-Description: Voicemail sound attachment.\r\n");
2001 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.gsm\"\r\n\r\n", msgnum);
2002 snprintf(fname, sizeof(fname), "%s.gsm", attach);
2003 base_encode(fname, p);
2005 fprintf(p, "\r\n\r\n--%s--\r\n.\r\n", bound);
2006 if (tmpfd > -1)
2007 close(tmpfd);
2008 unlink(newtmp);
2011 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)
2013 FILE *p=NULL;
2014 char tmp[80] = "/tmp/astmail-XXXXXX";
2015 char tmp2[256];
2017 if (vmu && ast_strlen_zero(vmu->email)) {
2018 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
2019 return(0);
2021 if (!strcmp(format, "wav49"))
2022 format = "WAV";
2023 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));
2024 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2025 command hangs */
2026 if ((p = vm_mkftemp(tmp)) == NULL) {
2027 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2028 return -1;
2029 } else {
2030 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
2031 fclose(p);
2032 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2033 ast_safe_system(tmp2);
2034 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2036 return 0;
2039 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)
2041 char date[256];
2042 char host[MAXHOSTNAMELEN]="";
2043 char who[256];
2044 char dur[256];
2045 char tmp[80] = "/tmp/astmail-XXXXXX";
2046 char tmp2[256];
2047 struct tm tm;
2048 FILE *p;
2050 if ((p = vm_mkftemp(tmp)) == NULL) {
2051 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2052 return -1;
2053 } else {
2054 gethostname(host, sizeof(host)-1);
2055 if (strchr(srcemail, '@'))
2056 ast_copy_string(who, srcemail, sizeof(who));
2057 else {
2058 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2060 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2061 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2062 fprintf(p, "Date: %s\n", date);
2064 if (*pagerfromstring) {
2065 struct ast_channel *ast;
2066 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2067 char *passdata;
2068 int vmlen = strlen(fromstring)*3 + 200;
2069 if ((passdata = alloca(vmlen))) {
2070 memset(passdata, 0, vmlen);
2071 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2072 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2073 fprintf(p, "From: %s <%s>\n", passdata, who);
2074 } else
2075 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2076 ast_channel_free(ast);
2077 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2078 } else
2079 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2080 fprintf(p, "To: %s\n", pager);
2081 if (pagersubject) {
2082 struct ast_channel *ast;
2083 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2084 char *passdata;
2085 int vmlen = strlen(pagersubject) * 3 + 200;
2086 if ((passdata = alloca(vmlen))) {
2087 memset(passdata, 0, vmlen);
2088 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2089 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2090 fprintf(p, "Subject: %s\n\n", passdata);
2091 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2092 ast_channel_free(ast);
2093 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2094 } else
2095 fprintf(p, "Subject: New VM\n\n");
2096 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2097 if (pagerbody) {
2098 struct ast_channel *ast;
2099 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2100 char *passdata;
2101 int vmlen = strlen(pagerbody)*3 + 200;
2102 if ((passdata = alloca(vmlen))) {
2103 memset(passdata, 0, vmlen);
2104 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2105 pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2106 fprintf(p, "%s\n", passdata);
2107 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2108 ast_channel_free(ast);
2109 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2110 } else {
2111 fprintf(p, "New %s long msg in box %s\n"
2112 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2114 fclose(p);
2115 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2116 ast_safe_system(tmp2);
2117 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
2119 return 0;
2122 static int get_date(char *s, int len)
2124 struct tm tm;
2125 time_t t;
2126 t = time(0);
2127 localtime_r(&t,&tm);
2128 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2131 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2133 int res;
2134 char fn[256];
2135 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2136 RETRIEVE(fn, -1);
2137 if (ast_fileexists(fn, NULL, NULL) > 0) {
2138 res = ast_stream_and_wait(chan, fn, chan->language, ecodes);
2139 if (res) {
2140 DISPOSE(fn, -1);
2141 return res;
2143 } else {
2144 /* Dispose just in case */
2145 DISPOSE(fn, -1);
2146 res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
2147 if (res)
2148 return res;
2149 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2150 if (res)
2151 return res;
2153 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
2154 return res;
2157 static void free_user(struct ast_vm_user *vmu)
2159 if (ast_test_flag(vmu, VM_ALLOCED))
2160 free(vmu);
2163 static void free_zone(struct vm_zone *z)
2165 free(z);
2168 static const char *mbox(int id)
2170 static const char *msgs[] = {
2171 "INBOX",
2172 "Old",
2173 "Work",
2174 "Family",
2175 "Friends",
2176 "Cust1",
2177 "Cust2",
2178 "Cust3",
2179 "Cust4",
2180 "Cust5",
2182 return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2185 #ifdef ODBC_STORAGE
2186 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2188 int x = -1;
2189 int res;
2190 SQLHSTMT stmt;
2191 char sql[256];
2192 char rowdata[20];
2193 char tmp[256]="";
2194 struct odbc_obj *obj;
2195 char *context;
2197 if (newmsgs)
2198 *newmsgs = 0;
2199 if (oldmsgs)
2200 *oldmsgs = 0;
2202 /* If no mailbox, return immediately */
2203 if (ast_strlen_zero(mailbox))
2204 return 0;
2206 ast_copy_string(tmp, mailbox, sizeof(tmp));
2208 context = strchr(tmp, '@');
2209 if (context) {
2210 *context = '\0';
2211 context++;
2212 } else
2213 context = "default";
2215 obj = ast_odbc_request_obj(odbc_database, 0);
2216 if (obj) {
2217 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2218 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2219 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2220 ast_odbc_release_obj(obj);
2221 goto yuck;
2223 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2224 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2225 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2226 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2227 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2228 ast_odbc_release_obj(obj);
2229 goto yuck;
2231 res = ast_odbc_smart_execute(obj, stmt);
2232 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2233 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2234 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2235 ast_odbc_release_obj(obj);
2236 goto yuck;
2238 res = SQLFetch(stmt);
2239 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2240 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2241 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2242 ast_odbc_release_obj(obj);
2243 goto yuck;
2245 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2246 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2247 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2248 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2249 ast_odbc_release_obj(obj);
2250 goto yuck;
2252 *newmsgs = atoi(rowdata);
2253 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2255 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2256 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2257 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2258 ast_odbc_release_obj(obj);
2259 goto yuck;
2261 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2262 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2263 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2264 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2265 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2266 ast_odbc_release_obj(obj);
2267 goto yuck;
2269 res = ast_odbc_smart_execute(obj, stmt);
2270 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2271 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2272 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2273 ast_odbc_release_obj(obj);
2274 goto yuck;
2276 res = SQLFetch(stmt);
2277 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2278 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2279 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2280 ast_odbc_release_obj(obj);
2281 goto yuck;
2283 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2284 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2285 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2286 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2287 ast_odbc_release_obj(obj);
2288 goto yuck;
2290 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2291 ast_odbc_release_obj(obj);
2292 *oldmsgs = atoi(rowdata);
2293 x = 0;
2294 } else
2295 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2297 yuck:
2298 return x;
2301 static int messagecount(const char *context, const char *mailbox, const char *folder)
2303 struct odbc_obj *obj = NULL;
2304 int nummsgs = 0;
2305 int res;
2306 SQLHSTMT stmt = NULL;
2307 char sql[256];
2308 char rowdata[20];
2309 if (!folder)
2310 folder = "INBOX";
2311 /* If no mailbox, return immediately */
2312 if (ast_strlen_zero(mailbox))
2313 return 0;
2315 obj = ast_odbc_request_obj(odbc_database, 0);
2316 if (obj) {
2317 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2318 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2319 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2320 goto yuck;
2322 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2323 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2324 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2325 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2326 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2327 goto yuck;
2329 res = ast_odbc_smart_execute(obj, stmt);
2330 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2331 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2332 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2333 goto yuck;
2335 res = SQLFetch(stmt);
2336 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2337 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2338 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2339 goto yuck;
2341 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2342 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2343 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2344 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2345 goto yuck;
2347 nummsgs = atoi(rowdata);
2348 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2349 } else
2350 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2352 yuck:
2353 if (obj)
2354 ast_odbc_release_obj(obj);
2355 return nummsgs;
2358 static int has_voicemail(const char *mailbox, const char *folder)
2360 char *context, tmp[256];
2361 ast_copy_string(tmp, mailbox, sizeof(tmp));
2362 if ((context = strchr(tmp, '@')))
2363 *context++ = '\0';
2364 else
2365 context = "default";
2367 if (messagecount(context, tmp, folder))
2368 return 1;
2369 else
2370 return 0;
2373 #elif defined(IMAP_STORAGE)
2375 static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms)
2377 char *myserveremail = serveremail;
2378 char fn[256];
2379 char mailbox[256];
2380 char *stringp;
2381 FILE *p=NULL;
2382 char tmp[80] = "/tmp/astmail-XXXXXX";
2383 long len;
2384 void *buf;
2385 STRING str;
2387 /* Attach only the first format */
2388 fmt = ast_strdupa(fmt);
2389 stringp = fmt;
2390 strsep(&stringp, "|");
2392 if (!ast_strlen_zero(vmu->serveremail))
2393 myserveremail = vmu->serveremail;
2395 make_file(fn, sizeof(fn), dir, msgnum);
2397 if (ast_strlen_zero(vmu->email))
2398 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2400 if (!strcmp(fmt, "wav49"))
2401 fmt = "WAV";
2402 if(option_debug > 2)
2403 ast_log(LOG_DEBUG, "Storing file '%s', format '%s'\n", fn, fmt);
2404 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2405 command hangs */
2406 if ((p = vm_mkftemp(tmp)) == NULL) {
2407 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2408 return -1;
2409 } else {
2410 make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, fmt, duration, 1, chan, NULL, 1);
2411 /* read mail file to memory */
2412 len = ftell(p);
2413 rewind(p);
2414 if((buf = ast_malloc(len+1)) == NIL) {
2415 ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2416 return -1;
2418 fread(buf, len, 1, p);
2419 ((char *)buf)[len] = '\0';
2420 INIT(&str, mail_string, buf, len);
2421 imap_mailbox_name(mailbox, vms, 0, 1);
2422 if(!mail_append(vms->mailstream, mailbox, &str))
2423 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2424 fclose(p);
2425 unlink(tmp);
2426 ast_free(buf);
2427 if(option_debug > 2)
2428 ast_log(LOG_DEBUG, "%s stored\n", fn);
2430 return 0;
2434 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2436 SEARCHPGM *pgm;
2437 SEARCHHEADER *hdr;
2439 struct ast_vm_user *vmu;
2440 struct vm_state *vms_p;
2441 char tmp[256]="";
2442 char *mb, *cur;
2443 char *mailboxnc;
2444 char *context;
2445 int ret = 0;
2446 if (newmsgs)
2447 *newmsgs = 0;
2448 if (oldmsgs)
2449 *oldmsgs = 0;
2451 if(option_debug > 2)
2452 ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox);
2453 /* If no mailbox, return immediately */
2454 if (ast_strlen_zero(mailbox))
2455 return 0;
2456 if (strchr(mailbox, ',')) {
2457 int tmpnew, tmpold;
2458 ast_copy_string(tmp, mailbox, sizeof(tmp));
2459 mb = tmp;
2460 ret = 0;
2461 while((cur = strsep(&mb, ", "))) {
2462 if (!ast_strlen_zero(cur)) {
2463 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2464 return -1;
2465 else {
2466 if (newmsgs)
2467 *newmsgs += tmpnew;
2468 if (oldmsgs)
2469 *oldmsgs += tmpold;
2473 return 0;
2475 ast_copy_string(tmp, mailbox, sizeof(tmp));
2476 context = strchr(tmp, '@');
2477 if (context) {
2478 *context = '\0';
2479 mailboxnc = tmp;
2480 context++;
2481 } else {
2482 context = "default";
2483 mailboxnc = (char *)mailbox;
2486 /* We have to get the user before we can open the stream! */
2487 /*ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2488 vmu = find_user(NULL, context, mailboxnc);
2489 if (!vmu) {
2490 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailboxnc,context);
2491 return -1;
2492 } else {
2493 /* No IMAP account available */
2494 if (vmu->imapuser[0] == '\0') {
2495 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2496 return -1;
2500 /* check if someone is accessing this box right now... */
2501 vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
2502 if (!vms_p) {
2503 vms_p = get_vm_state_by_mailbox(mailboxnc,1);
2505 if (vms_p) {
2506 if(option_debug > 2)
2507 ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
2508 *newmsgs = vms_p->newmessages;
2509 *oldmsgs = vms_p->oldmessages;
2510 return 0;
2513 /* add one if not there... */
2514 vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
2515 if (!vms_p) {
2516 vms_p = get_vm_state_by_mailbox(mailboxnc,0);
2519 if (!vms_p) {
2520 if(option_debug > 2)
2521 ast_log (LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2522 vms_p = (struct vm_state *)malloc(sizeof(struct vm_state));
2523 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2524 ast_copy_string(vms_p->username, mailboxnc, sizeof(vms_p->username)); /* save for access from interactive entry point */
2525 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2526 if(option_debug > 2)
2527 ast_log (LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2528 vms_p->updated = 1;
2529 vms_p->interactive = 0;
2530 /* set mailbox to INBOX! */
2531 ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
2532 init_vm_state(vms_p);
2533 vmstate_insert(vms_p);
2535 if (!vms_p->mailstream)
2536 ret = init_mailstream(vms_p, 0);
2537 if (!vms_p->mailstream) {
2538 ast_log (LOG_ERROR,"IMAP mailstream is NULL\n");
2539 return -1;
2541 if (newmsgs && ret==0 && vms_p->updated==1 ) {
2542 pgm = mail_newsearchpgm ();
2543 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2544 pgm->header = hdr;
2545 pgm->unseen = 1;
2546 pgm->seen = 0;
2547 pgm->undeleted = 1;
2548 pgm->deleted = 0;
2550 vms_p->vmArrayIndex = 0;
2552 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2553 *newmsgs = vms_p->vmArrayIndex;
2554 vms_p->newmessages = vms_p->vmArrayIndex;
2555 mail_free_searchpgm(&pgm);
2557 if (oldmsgs && ret==0 && vms_p->updated==1 ) {
2558 pgm = mail_newsearchpgm ();
2559 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2560 pgm->header = hdr;
2561 pgm->unseen = 0;
2562 pgm->seen = 1;
2563 pgm->deleted = 0;
2564 pgm->undeleted = 1;
2566 vms_p->vmArrayIndex = 0;
2568 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2569 *oldmsgs = vms_p->vmArrayIndex;
2570 vms_p->oldmessages = vms_p->vmArrayIndex;
2571 mail_free_searchpgm(&pgm);
2573 if (vms_p->updated == 1) { /* changes, so we did the searches above */
2574 vms_p->updated = 0;
2575 } else if (vms_p->updated > 1) { /* decrement delay count */
2576 vms_p->updated--;
2577 } else { /* no changes, so don't search */
2578 mail_ping(vms_p->mailstream);
2579 /* Keep the old data */
2580 *newmsgs = vms_p->newmessages;
2581 *oldmsgs = vms_p->oldmessages;
2583 return 0;
2586 static int has_voicemail(const char *mailbox, const char *folder)
2588 int newmsgs, oldmsgs;
2590 if(inboxcount(mailbox, &newmsgs, &oldmsgs))
2591 return folder? oldmsgs: newmsgs;
2592 else
2593 return 0;
2596 static int messagecount(const char *context, const char *mailbox, const char *folder)
2598 int newmsgs, oldmsgs;
2599 char tmp[256]="";
2601 if (ast_strlen_zero(mailbox))
2602 return 0;
2603 sprintf(tmp,"%s@%s", mailbox, ast_strlen_zero(context)? "default": context);
2605 if(inboxcount(tmp, &newmsgs, &oldmsgs))
2606 return folder? oldmsgs: newmsgs;
2607 else
2608 return 0;
2611 #endif
2612 #ifndef IMAP_STORAGE
2613 /* copy message only used by file storage */
2614 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)
2617 char fromdir[256], todir[256], frompath[256], topath[256];
2618 const char *frombox = mbox(imbox);
2619 int recipmsgnum;
2621 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2623 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2625 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2626 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2628 if (vm_lock_path(todir))
2629 return ERROR_LOCK_PATH;
2631 recipmsgnum = 0;
2632 do {
2633 make_file(topath, sizeof(topath), todir, recipmsgnum);
2634 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
2635 break;
2636 recipmsgnum++;
2637 } while (recipmsgnum < recip->maxmsg);
2638 if (recipmsgnum < recip->maxmsg) {
2639 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2640 } else {
2641 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2643 ast_unlock_path(todir);
2644 notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2646 return 0;
2648 #endif
2649 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2650 static int messagecount(const char *context, const char *mailbox, const char *folder)
2652 return __has_voicemail(context, mailbox, folder, 0);
2656 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2658 DIR *dir;
2659 struct dirent *de;
2660 char fn[256];
2661 int ret = 0;
2662 if (!folder)
2663 folder = "INBOX";
2664 /* If no mailbox, return immediately */
2665 if (ast_strlen_zero(mailbox))
2666 return 0;
2667 if (!context)
2668 context = "default";
2669 snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2670 dir = opendir(fn);
2671 if (!dir)
2672 return 0;
2673 while ((de = readdir(dir))) {
2674 if (!strncasecmp(de->d_name, "msg", 3)) {
2675 if (shortcircuit) {
2676 ret = 1;
2677 break;
2678 } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2679 ret++;
2682 closedir(dir);
2683 return ret;
2687 static int has_voicemail(const char *mailbox, const char *folder)
2689 char tmp[256], *tmp2 = tmp, *mbox, *context;
2690 ast_copy_string(tmp, mailbox, sizeof(tmp));
2691 while ((mbox = strsep(&tmp2, ","))) {
2692 if ((context = strchr(mbox, '@')))
2693 *context++ = '\0';
2694 else
2695 context = "default";
2696 if (__has_voicemail(context, mbox, folder, 1))
2697 return 1;
2699 return 0;
2703 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2705 char tmp[256];
2706 char *context;
2708 if (newmsgs)
2709 *newmsgs = 0;
2710 if (oldmsgs)
2711 *oldmsgs = 0;
2712 /* If no mailbox, return immediately */
2713 if (ast_strlen_zero(mailbox))
2714 return 0;
2715 if (strchr(mailbox, ',')) {
2716 int tmpnew, tmpold;
2717 char *mb, *cur;
2719 ast_copy_string(tmp, mailbox, sizeof(tmp));
2720 mb = tmp;
2721 while ((cur = strsep(&mb, ", "))) {
2722 if (!ast_strlen_zero(cur)) {
2723 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2724 return -1;
2725 else {
2726 if (newmsgs)
2727 *newmsgs += tmpnew;
2728 if (oldmsgs)
2729 *oldmsgs += tmpold;
2733 return 0;
2735 ast_copy_string(tmp, mailbox, sizeof(tmp));
2736 context = strchr(tmp, '@');
2737 if (context) {
2738 *context = '\0';
2739 context++;
2740 } else
2741 context = "default";
2742 if (newmsgs)
2743 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2744 if (oldmsgs)
2745 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2746 return 0;
2749 #endif
2751 static void run_externnotify(char *context, char *extension)
2753 char arguments[255];
2754 char ext_context[256] = "";
2755 int newvoicemails = 0, oldvoicemails = 0;
2756 struct ast_smdi_mwi_message *mwi_msg;
2758 if (!ast_strlen_zero(context))
2759 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2760 else
2761 ast_copy_string(ext_context, extension, sizeof(ext_context));
2763 if (!strcasecmp(externnotify, "smdi")) {
2764 if (ast_app_has_voicemail(ext_context, NULL))
2765 ast_smdi_mwi_set(smdi_iface, extension);
2766 else
2767 ast_smdi_mwi_unset(smdi_iface, extension);
2769 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2770 ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2771 if (!strncmp(mwi_msg->cause, "INV", 3))
2772 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2773 else if (!strncmp(mwi_msg->cause, "BLK", 3))
2774 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2775 ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2776 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2777 } else {
2778 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2780 } else if (!ast_strlen_zero(externnotify)) {
2781 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2782 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2783 } else {
2784 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2785 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2786 ast_safe_system(arguments);
2791 struct leave_vm_options {
2792 unsigned int flags;
2793 signed char record_gain;
2796 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2798 #ifdef IMAP_STORAGE
2799 int newmsgs, oldmsgs;
2800 struct vm_state *vms = NULL;
2801 #endif
2802 char tmptxtfile[256], txtfile[256];
2803 char callerid[256];
2804 FILE *txt;
2805 char date[256];
2806 int txtdes;
2807 int res = 0;
2808 int msgnum;
2809 int duration = 0;
2810 int ausemacro = 0;
2811 int ousemacro = 0;
2812 int ouseexten = 0;
2813 char dir[256], tmpdir[260];
2814 char fn[256];
2815 char prefile[256]="";
2816 char tempfile[256]="";
2817 char ext_context[256] = "";
2818 char fmt[80];
2819 char *context;
2820 char ecodes[16] = "#";
2821 char tmp[256] = "", *tmpptr;
2822 struct ast_vm_user *vmu;
2823 struct ast_vm_user svm;
2824 const char *category = NULL;
2826 ast_copy_string(tmp, ext, sizeof(tmp));
2827 ext = tmp;
2828 context = strchr(tmp, '@');
2829 if (context) {
2830 *context++ = '\0';
2831 tmpptr = strchr(context, '&');
2832 } else {
2833 tmpptr = strchr(ext, '&');
2836 if (tmpptr)
2837 *tmpptr++ = '\0';
2839 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2841 if(option_debug > 2)
2842 ast_log(LOG_DEBUG, "Before find_user\n");
2843 if (!(vmu = find_user(&svm, context, ext))) {
2844 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2845 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2846 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2847 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2848 return res;
2851 /* Setup pre-file if appropriate */
2852 if (strcmp(vmu->context, "default"))
2853 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2854 else
2855 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2856 if (ast_test_flag(options, OPT_BUSY_GREETING))
2857 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2858 else if (ast_test_flag(options, OPT_UNAVAIL_GREETING))
2859 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2860 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2861 RETRIEVE(tempfile, -1);
2862 if (ast_fileexists(tempfile, NULL, NULL) > 0)
2863 ast_copy_string(prefile, tempfile, sizeof(prefile));
2864 DISPOSE(tempfile, -1);
2865 /* It's easier just to try to make it than to check for its existence */
2866 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2867 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
2869 /* Check current or macro-calling context for special extensions */
2870 if (ast_test_flag(vmu, VM_OPERATOR)) {
2871 if (!ast_strlen_zero(vmu->exit)) {
2872 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
2873 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2874 ouseexten = 1;
2876 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
2877 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2878 ouseexten = 1;
2880 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2881 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2882 ousemacro = 1;
2886 if (!ast_strlen_zero(vmu->exit)) {
2887 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2888 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2889 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2890 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2891 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2892 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2893 ausemacro = 1;
2896 /* Play the beginning intro if desired */
2897 if (!ast_strlen_zero(prefile)) {
2898 RETRIEVE(prefile, -1);
2899 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2900 if (ast_streamfile(chan, prefile, chan->language) > -1)
2901 res = ast_waitstream(chan, ecodes);
2902 } else {
2903 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2904 res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2906 DISPOSE(prefile, -1);
2907 if (res < 0) {
2908 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2909 free_user(vmu);
2910 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2911 return -1;
2914 if (res == '#') {
2915 /* On a '#' we skip the instructions */
2916 ast_set_flag(options, OPT_SILENT);
2917 res = 0;
2919 if (!res && !ast_test_flag(options, OPT_SILENT)) {
2920 res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
2921 if (res == '#') {
2922 ast_set_flag(options, OPT_SILENT);
2923 res = 0;
2926 if (res > 0)
2927 ast_stopstream(chan);
2928 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2929 other than the operator -- an automated attendant or mailbox login for example */
2930 if (res == '*') {
2931 chan->exten[0] = 'a';
2932 chan->exten[1] = '\0';
2933 if (!ast_strlen_zero(vmu->exit)) {
2934 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2935 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2936 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2938 chan->priority = 0;
2939 free_user(vmu);
2940 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2941 return 0;
2944 /* Check for a '0' here */
2945 if (res == '0') {
2946 transfer:
2947 if (ouseexten || ousemacro) {
2948 chan->exten[0] = 'o';
2949 chan->exten[1] = '\0';
2950 if (!ast_strlen_zero(vmu->exit)) {
2951 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2952 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2953 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2955 ast_play_and_wait(chan, "transfer");
2956 chan->priority = 0;
2957 free_user(vmu);
2958 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2960 return 0;
2962 if (res < 0) {
2963 free_user(vmu);
2964 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2965 return -1;
2967 /* The meat of recording the message... All the announcements and beeps have been played*/
2968 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2969 if (!ast_strlen_zero(fmt)) {
2970 msgnum = 0;
2972 #ifdef IMAP_STORAGE
2973 /* Is ext a mailbox? */
2974 /* must open stream for this user to get info! */
2975 vms = get_vm_state_by_mailbox(ext,0);
2976 if (vms) {
2977 if(option_debug > 2)
2978 ast_log(LOG_DEBUG, "Using vm_state, interactive set to %d.\n",vms->interactive);
2979 newmsgs = vms->newmessages++;
2980 oldmsgs = vms->oldmessages;
2981 } else {
2982 res = inboxcount(ext, &newmsgs, &oldmsgs);
2983 if(res < 0) {
2984 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
2985 return -1;
2987 vms = get_vm_state_by_mailbox(ext,0);
2989 /* here is a big difference! We add one to it later */
2990 msgnum = newmsgs + oldmsgs;
2991 if (option_debug > 2)
2992 ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
2993 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
2994 /* set variable for compatability */
2995 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
2997 /* Check if mailbox is full */
2998 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
2999 if(option_debug)
3000 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
3001 ast_play_and_wait(chan, "vm-mailboxfull");
3002 return -1;
3004 /* here is a big difference! We add one to it later */
3005 msgnum = newmsgs + oldmsgs;
3006 if (option_debug > 2)
3007 ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3008 #else
3009 if (count_messages(vmu, dir) >= vmu->maxmsg) {
3010 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3011 if (!res)
3012 res = ast_waitstream(chan, "");
3013 ast_log(LOG_WARNING, "No more messages possible\n");
3014 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3015 goto leave_vm_out;
3018 #endif
3019 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
3020 txtdes = mkstemp(tmptxtfile);
3021 if (txtdes < 0) {
3022 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3023 if (!res)
3024 res = ast_waitstream(chan, "");
3025 ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
3026 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3027 goto leave_vm_out;
3030 /* Now play the beep once we have the message number for our next message. */
3031 if (res >= 0) {
3032 /* Unless we're *really* silent, try to send the beep */
3033 res = ast_stream_and_wait(chan, "beep", chan->language, "");
3036 /* Store information */
3037 txt = fdopen(txtdes, "w+");
3038 if (txt) {
3039 get_date(date, sizeof(date));
3040 fprintf(txt,
3041 ";\n"
3042 "; Message Information file\n"
3043 ";\n"
3044 "[message]\n"
3045 "origmailbox=%s\n"
3046 "context=%s\n"
3047 "macrocontext=%s\n"
3048 "exten=%s\n"
3049 "priority=%d\n"
3050 "callerchan=%s\n"
3051 "callerid=%s\n"
3052 "origdate=%s\n"
3053 "origtime=%ld\n"
3054 "category=%s\n",
3055 ext,
3056 chan->context,
3057 chan->macrocontext,
3058 chan->exten,
3059 chan->priority,
3060 chan->name,
3061 ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
3062 date, (long)time(NULL),
3063 category ? category : "");
3064 } else
3065 ast_log(LOG_WARNING, "Error opening text file for output\n");
3066 res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain);
3068 if (txt) {
3069 if (duration < vmminmessage) {
3070 if (option_verbose > 2)
3071 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
3072 ast_filedelete(tmptxtfile, NULL);
3073 unlink(tmptxtfile);
3074 } else {
3075 fprintf(txt, "duration=%d\n", duration);
3076 fclose(txt);
3077 if (vm_lock_path(dir)) {
3078 ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
3079 /* Delete files */
3080 ast_filedelete(tmptxtfile, NULL);
3081 unlink(tmptxtfile);
3082 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
3083 if (option_debug)
3084 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
3085 unlink(tmptxtfile);
3086 ast_unlock_path(dir);
3087 } else {
3088 for (;;) {
3089 make_file(fn, sizeof(fn), dir, msgnum);
3090 if (!EXISTS(dir, msgnum, fn, NULL))
3091 break;
3092 msgnum++;
3095 /* assign a variable with the name of the voicemail file */
3096 #ifndef IMAP_STORAGE
3097 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3098 #else
3099 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3100 #endif
3102 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3103 ast_filerename(tmptxtfile, fn, NULL);
3104 rename(tmptxtfile, txtfile);
3106 ast_unlock_path(dir);
3107 #ifndef IMAP_STORAGE
3108 /* Are there to be more recipients of this message? */
3109 while (tmpptr) {
3110 struct ast_vm_user recipu, *recip;
3111 char *exten, *context;
3113 exten = strsep(&tmpptr, "&");
3114 context = strchr(exten, '@');
3115 if (context) {
3116 *context = '\0';
3117 context++;
3119 if ((recip = find_user(&recipu, context, exten))) {
3120 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
3121 free_user(recip);
3124 #endif
3125 if (ast_fileexists(fn, NULL, NULL)) {
3126 STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
3127 notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
3128 DISPOSE(dir, msgnum);
3133 if (res == '0') {
3134 goto transfer;
3135 } else if (res > 0)
3136 res = 0;
3138 if (duration < vmminmessage)
3139 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3140 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3141 else
3142 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3143 } else
3144 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3145 leave_vm_out:
3146 free_user(vmu);
3148 return res;
3151 #ifndef IMAP_STORAGE
3152 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3154 /* we know max messages, so stop process when number is hit */
3156 int x,dest;
3157 char sfn[256];
3158 char dfn[256];
3160 if (vm_lock_path(dir))
3161 return ERROR_LOCK_PATH;
3163 for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3164 make_file(sfn, sizeof(sfn), dir, x);
3165 if (EXISTS(dir, x, sfn, NULL)) {
3167 if (x != dest) {
3168 make_file(dfn, sizeof(dfn), dir, dest);
3169 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3172 dest++;
3175 ast_unlock_path(dir);
3177 return 0;
3179 #endif
3181 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3183 int d;
3184 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
3185 return d;
3188 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3190 #ifdef IMAP_STORAGE
3191 /* we must use mbox(x) folder names, and copy the message there */
3192 /* simple. huh? */
3193 char dbox[256];
3194 long res;
3195 char sequence[10];
3197 /* if save to Old folder, just leave in INBOX */
3198 if (box == 1) return 10;
3199 /* get the real IMAP message number for this message */
3200 sprintf(sequence,"%ld",vms->msgArray[msg]);
3201 imap_mailbox_name(dbox, vms, box, 1);
3202 if(option_debug > 2)
3203 ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,dbox);
3204 res = mail_copy(vms->mailstream,sequence,dbox);
3205 if (res == 1) return 0;
3206 return 1;
3207 #else
3208 char *dir = vms->curdir;
3209 char *username = vms->username;
3210 char *context = vmu->context;
3211 char sfn[256];
3212 char dfn[256];
3213 char ddir[256];
3214 const char *dbox = mbox(box);
3215 int x;
3216 make_file(sfn, sizeof(sfn), dir, msg);
3217 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
3219 if (vm_lock_path(ddir))
3220 return ERROR_LOCK_PATH;
3222 for (x = 0; x < vmu->maxmsg; x++) {
3223 make_file(dfn, sizeof(dfn), ddir, x);
3224 if (!EXISTS(ddir, x, dfn, NULL))
3225 break;
3227 if (x >= vmu->maxmsg) {
3228 ast_unlock_path(ddir);
3229 return -1;
3231 if (strcmp(sfn, dfn)) {
3232 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
3234 ast_unlock_path(ddir);
3235 #endif
3236 return 0;
3239 static int adsi_logo(unsigned char *buf)
3241 int bytes = 0;
3242 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
3243 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
3244 return bytes;
3247 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
3249 unsigned char buf[256];
3250 int bytes=0;
3251 int x;
3252 char num[5];
3254 *useadsi = 0;
3255 bytes += ast_adsi_data_mode(buf + bytes);
3256 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3258 bytes = 0;
3259 bytes += adsi_logo(buf);
3260 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3261 #ifdef DISPLAY
3262 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
3263 #endif
3264 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3265 bytes += ast_adsi_data_mode(buf + bytes);
3266 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3268 if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
3269 bytes = 0;
3270 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
3271 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3272 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3273 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3274 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3275 return 0;
3278 #ifdef DISPLAY
3279 /* Add a dot */
3280 bytes = 0;
3281 bytes += ast_adsi_logo(buf);
3282 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3283 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
3284 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3285 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3286 #endif
3287 bytes = 0;
3288 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
3289 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
3290 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
3291 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
3292 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
3293 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
3294 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3296 #ifdef DISPLAY
3297 /* Add another dot */
3298 bytes = 0;
3299 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
3300 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3302 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3303 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3304 #endif
3306 bytes = 0;
3307 /* These buttons we load but don't use yet */
3308 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
3309 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
3310 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
3311 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
3312 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
3313 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
3314 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3316 #ifdef DISPLAY
3317 /* Add another dot */
3318 bytes = 0;
3319 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
3320 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3321 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3322 #endif
3324 bytes = 0;
3325 for (x=0;x<5;x++) {
3326 snprintf(num, sizeof(num), "%d", x);
3327 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
3329 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
3330 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3332 #ifdef DISPLAY
3333 /* Add another dot */
3334 bytes = 0;
3335 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
3336 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3337 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3338 #endif
3340 if (ast_adsi_end_download(chan)) {
3341 bytes = 0;
3342 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
3343 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3344 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3345 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3346 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3347 return 0;
3349 bytes = 0;
3350 bytes += ast_adsi_download_disconnect(buf + bytes);
3351 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3352 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3354 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
3356 #ifdef DISPLAY
3357 /* Add last dot */
3358 bytes = 0;
3359 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
3360 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3361 #endif
3362 ast_log(LOG_DEBUG, "Restarting session...\n");
3364 bytes = 0;
3365 /* Load the session now */
3366 if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
3367 *useadsi = 1;
3368 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
3369 } else
3370 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
3372 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3373 return 0;
3376 static void adsi_begin(struct ast_channel *chan, int *useadsi)
3378 int x;
3379 if (!ast_adsi_available(chan))
3380 return;
3381 x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
3382 if (x < 0)
3383 return;
3384 if (!x) {
3385 if (adsi_load_vmail(chan, useadsi)) {
3386 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
3387 return;
3389 } else
3390 *useadsi = 1;
3393 static void adsi_login(struct ast_channel *chan)
3395 unsigned char buf[256];
3396 int bytes=0;
3397 unsigned char keys[8];
3398 int x;
3399 if (!ast_adsi_available(chan))
3400 return;
3402 for (x=0;x<8;x++)
3403 keys[x] = 0;
3404 /* Set one key for next */
3405 keys[3] = ADSI_KEY_APPS + 3;
3407 bytes += adsi_logo(buf + bytes);
3408 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
3409 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
3410 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3411 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
3412 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
3413 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
3414 bytes += ast_adsi_set_keys(buf + bytes, keys);
3415 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3416 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3419 static void adsi_password(struct ast_channel *chan)
3421 unsigned char buf[256];
3422 int bytes=0;
3423 unsigned char keys[8];
3424 int x;
3425 if (!ast_adsi_available(chan))
3426 return;
3428 for (x=0;x<8;x++)
3429 keys[x] = 0;
3430 /* Set one key for next */
3431 keys[3] = ADSI_KEY_APPS + 3;
3433 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3434 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
3435 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
3436 bytes += ast_adsi_set_keys(buf + bytes, keys);
3437 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3438 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3441 static void adsi_folders(struct ast_channel *chan, int start, char *label)
3443 unsigned char buf[256];
3444 int bytes=0;
3445 unsigned char keys[8];
3446 int x,y;
3448 if (!ast_adsi_available(chan))
3449 return;
3451 for (x=0;x<5;x++) {
3452 y = ADSI_KEY_APPS + 12 + start + x;
3453 if (y > ADSI_KEY_APPS + 12 + 4)
3454 y = 0;
3455 keys[x] = ADSI_KEY_SKT | y;
3457 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
3458 keys[6] = 0;
3459 keys[7] = 0;
3461 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
3462 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
3463 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3464 bytes += ast_adsi_set_keys(buf + bytes, keys);
3465 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3467 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3470 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
3472 int bytes=0;
3473 unsigned char buf[256];
3474 char buf1[256], buf2[256];
3475 char fn2[256];
3477 char cid[256]="";
3478 char *val;
3479 char *name, *num;
3480 char datetime[21]="";
3481 FILE *f;
3483 unsigned char keys[8];
3485 int x;
3487 if (!ast_adsi_available(chan))
3488 return;
3490 /* Retrieve important info */
3491 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
3492 f = fopen(fn2, "r");
3493 if (f) {
3494 while (!feof(f)) {
3495 fgets((char *)buf, sizeof(buf), f);
3496 if (!feof(f)) {
3497 char *stringp=NULL;
3498 stringp = (char *)buf;
3499 strsep(&stringp, "=");
3500 val = strsep(&stringp, "=");
3501 if (!ast_strlen_zero(val)) {
3502 if (!strcmp((char *)buf, "callerid"))
3503 ast_copy_string(cid, val, sizeof(cid));
3504 if (!strcmp((char *)buf, "origdate"))
3505 ast_copy_string(datetime, val, sizeof(datetime));
3509 fclose(f);
3511 /* New meaning for keys */
3512 for (x=0;x<5;x++)
3513 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3514 keys[6] = 0x0;
3515 keys[7] = 0x0;
3517 if (!vms->curmsg) {
3518 /* No prev key, provide "Folder" instead */
3519 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3521 if (vms->curmsg >= vms->lastmsg) {
3522 /* If last message ... */
3523 if (vms->curmsg) {
3524 /* but not only message, provide "Folder" instead */
3525 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3526 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3528 } else {
3529 /* Otherwise if only message, leave blank */
3530 keys[3] = 1;
3534 if (!ast_strlen_zero(cid)) {
3535 ast_callerid_parse(cid, &name, &num);
3536 if (!name)
3537 name = num;
3538 } else
3539 name = "Unknown Caller";
3541 /* If deleted, show "undeleted" */
3543 if (vms->deleted[vms->curmsg])
3544 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3546 /* Except "Exit" */
3547 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3548 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
3549 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
3550 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
3552 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3553 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3554 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
3555 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
3556 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3557 bytes += ast_adsi_set_keys(buf + bytes, keys);
3558 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3560 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3563 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
3565 int bytes=0;
3566 unsigned char buf[256];
3567 unsigned char keys[8];
3569 int x;
3571 if (!ast_adsi_available(chan))
3572 return;
3574 /* New meaning for keys */
3575 for (x=0;x<5;x++)
3576 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3578 keys[6] = 0x0;
3579 keys[7] = 0x0;
3581 if (!vms->curmsg) {
3582 /* No prev key, provide "Folder" instead */
3583 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3585 if (vms->curmsg >= vms->lastmsg) {
3586 /* If last message ... */
3587 if (vms->curmsg) {
3588 /* but not only message, provide "Folder" instead */
3589 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3590 } else {
3591 /* Otherwise if only message, leave blank */
3592 keys[3] = 1;
3596 /* If deleted, show "undeleted" */
3597 if (vms->deleted[vms->curmsg])
3598 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3600 /* Except "Exit" */
3601 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3602 bytes += ast_adsi_set_keys(buf + bytes, keys);
3603 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3605 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3608 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
3610 unsigned char buf[256] = "";
3611 char buf1[256] = "", buf2[256] = "";
3612 int bytes=0;
3613 unsigned char keys[8];
3614 int x;
3616 char *newm = (vms->newmessages == 1) ? "message" : "messages";
3617 char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
3618 if (!ast_adsi_available(chan))
3619 return;
3620 if (vms->newmessages) {
3621 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
3622 if (vms->oldmessages) {
3623 strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
3624 snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
3625 } else {
3626 snprintf(buf2, sizeof(buf2), "%s.", newm);
3628 } else if (vms->oldmessages) {
3629 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
3630 snprintf(buf2, sizeof(buf2), "%s.", oldm);
3631 } else {
3632 strcpy(buf1, "You have no messages.");
3633 buf2[0] = ' ';
3634 buf2[1] = '\0';
3636 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3637 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3638 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3640 for (x=0;x<6;x++)
3641 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3642 keys[6] = 0;
3643 keys[7] = 0;
3645 /* Don't let them listen if there are none */
3646 if (vms->lastmsg < 0)
3647 keys[0] = 1;
3648 bytes += ast_adsi_set_keys(buf + bytes, keys);
3650 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3652 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3655 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
3657 unsigned char buf[256] = "";
3658 char buf1[256] = "", buf2[256] = "";
3659 int bytes=0;
3660 unsigned char keys[8];
3661 int x;
3663 char *mess = (vms->lastmsg == 0) ? "message" : "messages";
3665 if (!ast_adsi_available(chan))
3666 return;
3668 /* Original command keys */
3669 for (x=0;x<6;x++)
3670 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3672 keys[6] = 0;
3673 keys[7] = 0;
3675 if ((vms->lastmsg + 1) < 1)
3676 keys[0] = 0;
3678 snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
3679 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
3681 if (vms->lastmsg + 1)
3682 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
3683 else
3684 strcpy(buf2, "no messages.");
3685 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3686 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3687 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
3688 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3689 bytes += ast_adsi_set_keys(buf + bytes, keys);
3691 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3693 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3698 static void adsi_clear(struct ast_channel *chan)
3700 char buf[256];
3701 int bytes=0;
3702 if (!ast_adsi_available(chan))
3703 return;
3704 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3705 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3707 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3711 static void adsi_goodbye(struct ast_channel *chan)
3713 unsigned char buf[256];
3714 int bytes=0;
3716 if (!ast_adsi_available(chan))
3717 return;
3718 bytes += adsi_logo(buf + bytes);
3719 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
3720 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
3721 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3722 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3724 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3727 /*--- get_folder: Folder menu ---*/
3728 /* Plays "press 1 for INBOX messages" etc
3729 Should possibly be internationalized
3731 static int get_folder(struct ast_channel *chan, int start)
3733 int x;
3734 int d;
3735 char fn[256];
3736 d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
3737 if (d)
3738 return d;
3739 for (x = start; x< 5; x++) { /* For all folders */
3740 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
3741 return d;
3742 d = ast_play_and_wait(chan, "vm-for"); /* "for" */
3743 if (d)
3744 return d;
3745 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
3746 d = vm_play_folder_name(chan, fn);
3747 if (d)
3748 return d;
3749 d = ast_waitfordigit(chan, 500);
3750 if (d)
3751 return d;
3753 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
3754 if (d)
3755 return d;
3756 d = ast_waitfordigit(chan, 4000);
3757 return d;
3760 static int get_folder2(struct ast_channel *chan, char *fn, int start)
3762 int res = 0;
3763 res = ast_play_and_wait(chan, fn); /* Folder name */
3764 while (((res < '0') || (res > '9')) &&
3765 (res != '#') && (res >= 0)) {
3766 res = get_folder(chan, 0);
3768 return res;
3771 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfmts,
3772 char *context, signed char record_gain, long *duration, struct vm_state *vms)
3774 int cmd = 0;
3775 int retries = 0;
3776 signed char zero_gain = 0;
3778 while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
3779 if (cmd)
3780 retries = 0;
3781 switch (cmd) {
3782 case '1':
3783 /* prepend a message to the current message, update the metadata and return */
3785 char msgfile[PATH_MAX];
3786 char textfile[PATH_MAX];
3787 int prepend_duration = 0;
3788 struct ast_config *msg_cfg;
3789 const char *duration_str;
3791 make_file(msgfile, sizeof(msgfile), curdir, curmsg);
3792 strcpy(textfile, msgfile);
3793 strncat(textfile, ".txt", sizeof(textfile) - 1);
3794 *duration = 0;
3796 /* if we can't read the message metadata, stop now */
3797 if (!(msg_cfg = ast_config_load(textfile))) {
3798 cmd = 0;
3799 break;
3802 if (record_gain)
3803 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
3805 cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vmfmts, &prepend_duration, 1, silencethreshold, maxsilence);
3806 if (record_gain)
3807 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
3810 if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
3811 *duration = atoi(duration_str);
3813 if (prepend_duration) {
3814 struct ast_category *msg_cat;
3815 /* need enough space for a maximum-length message duration */
3816 char duration_str[12];
3818 *duration += prepend_duration;
3819 msg_cat = ast_category_get(msg_cfg, "message");
3820 snprintf(duration_str, 11, "%ld", *duration);
3821 if (!ast_variable_update(msg_cat, "duration", duration_str, NULL)) {
3822 config_text_file_save(textfile, msg_cfg, "app_voicemail");
3823 STORE(curdir, vmu->mailbox, context, curmsg, chan, vmu, vmfmts, *duration, vms);
3827 ast_config_destroy(msg_cfg);
3829 break;
3831 case '2':
3832 cmd = 't';
3833 break;
3834 case '*':
3835 cmd = '*';
3836 break;
3837 default:
3838 cmd = ast_play_and_wait(chan,"vm-forwardoptions");
3839 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
3840 if (!cmd)
3841 cmd = ast_play_and_wait(chan,"vm-starmain");
3842 /* "press star to return to the main menu" */
3843 if (!cmd)
3844 cmd = ast_waitfordigit(chan,6000);
3845 if (!cmd)
3846 retries++;
3847 if (retries > 3)
3848 cmd = 't';
3851 if (cmd == 't' || cmd == 'S')
3852 cmd = 0;
3853 return cmd;
3856 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
3858 char todir[256], fn[256], ext_context[256], *stringp;
3859 int newmsgs = 0, oldmsgs = 0;
3860 const char *category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3862 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
3863 make_file(fn, sizeof(fn), todir, msgnum);
3864 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
3866 if (!ast_strlen_zero(vmu->attachfmt)) {
3867 if (strstr(fmt, vmu->attachfmt)) {
3868 fmt = vmu->attachfmt;
3869 } else {
3870 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);
3874 /* Attach only the first format */
3875 fmt = ast_strdupa(fmt);
3876 stringp = fmt;
3877 strsep(&stringp, "|");
3879 if (!ast_strlen_zero(vmu->email)) {
3880 int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
3881 char *myserveremail = serveremail;
3882 attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
3883 if (!ast_strlen_zero(vmu->serveremail))
3884 myserveremail = vmu->serveremail;
3885 /*XXX possible imap issue, should category be NULL XXX*/
3886 sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail, chan, category);
3889 if (!ast_strlen_zero(vmu->pager)) {
3890 char *myserveremail = serveremail;
3891 if (!ast_strlen_zero(vmu->serveremail))
3892 myserveremail = vmu->serveremail;
3893 sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, duration, vmu, category);
3896 if (ast_test_flag(vmu, VM_DELETE)) {
3897 DELETE(todir, msgnum, fn);
3900 #ifdef IMAP_STORAGE
3901 DELETE(todir, msgnum, fn);
3902 #endif
3903 /* Leave voicemail for someone */
3904 if (ast_app_has_voicemail(ext_context, NULL)) {
3905 ast_app_inboxcount(ext_context, &newmsgs, &oldmsgs);
3907 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);
3908 run_externnotify(vmu->context, vmu->mailbox);
3909 return 0;
3912 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)
3914 #ifdef IMAP_STORAGE
3915 BODY *body;
3916 char *header_content;
3917 char *temp;
3918 char todir[256];
3919 int todircount=0;
3920 int duration;
3921 #endif
3922 char username[70]="";
3923 int res = 0, cmd = 0;
3924 struct ast_vm_user *receiver = NULL, *vmtmp;
3925 AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
3926 char *stringp;
3927 const char *s;
3928 int saved_messages = 0, found = 0;
3929 int valid_extensions = 0;
3930 char *dir;
3931 int curmsg;
3933 if (vms == NULL) return -1;
3934 dir = vms->curdir;
3935 curmsg = vms->curmsg;
3937 while (!res && !valid_extensions) {
3938 int use_directory = 0;
3939 if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
3940 int done = 0;
3941 int retries = 0;
3942 cmd=0;
3943 while ((cmd >= 0) && !done ){
3944 if (cmd)
3945 retries = 0;
3946 switch (cmd) {
3947 case '1':
3948 use_directory = 0;
3949 done = 1;
3950 break;
3951 case '2':
3952 use_directory = 1;
3953 done=1;
3954 break;
3955 case '*':
3956 cmd = 't';
3957 done = 1;
3958 break;
3959 default:
3960 /* Press 1 to enter an extension press 2 to use the directory */
3961 cmd = ast_play_and_wait(chan,"vm-forward");
3962 if (!cmd)
3963 cmd = ast_waitfordigit(chan,3000);
3964 if (!cmd)
3965 retries++;
3966 if (retries > 3)
3968 cmd = 't';
3969 done = 1;
3974 if (cmd < 0 || cmd == 't')
3975 break;
3978 if (use_directory) {
3979 /* use app_directory */
3981 char old_context[sizeof(chan->context)];
3982 char old_exten[sizeof(chan->exten)];
3983 int old_priority;
3984 struct ast_app* app;
3987 app = pbx_findapp("Directory");
3988 if (app) {
3989 char vmcontext[256];
3990 /* make backup copies */
3991 memcpy(old_context, chan->context, sizeof(chan->context));
3992 memcpy(old_exten, chan->exten, sizeof(chan->exten));
3993 old_priority = chan->priority;
3995 /* call the the Directory, changes the channel */
3996 sprintf(vmcontext, "%s||v", context ? context : "default");
3997 res = pbx_exec(chan, app, vmcontext);
3999 ast_copy_string(username, chan->exten, sizeof(username));
4001 /* restore the old context, exten, and priority */
4002 memcpy(chan->context, old_context, sizeof(chan->context));
4003 memcpy(chan->exten, old_exten, sizeof(chan->exten));
4004 chan->priority = old_priority;
4006 } else {
4007 ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
4008 ast_clear_flag((&globalflags), VM_DIRECFORWARD);
4010 } else {
4011 /* Ask for an extension */
4012 res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
4013 if (res)
4014 break;
4015 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
4016 break;
4019 /* start all over if no username */
4020 if (ast_strlen_zero(username))
4021 continue;
4022 stringp = username;
4023 s = strsep(&stringp, "*");
4024 /* start optimistic */
4025 valid_extensions = 1;
4026 while (s) {
4027 /* Don't forward to ourselves. find_user is going to malloc since we have a NULL as first argument */
4028 if (strcmp(s,sender->mailbox) && (receiver = find_user(NULL, context, s))) {
4029 AST_LIST_INSERT_HEAD(&extensions, receiver, list);
4030 found++;
4031 } else {
4032 valid_extensions = 0;
4033 break;
4035 s = strsep(&stringp, "*");
4037 /* break from the loop of reading the extensions */
4038 if (valid_extensions)
4039 break;
4040 /* "I am sorry, that's not a valid extension. Please try again." */
4041 res = ast_play_and_wait(chan, "pbx-invalid");
4043 /* check if we're clear to proceed */
4044 if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
4045 return res;
4046 if (flag==1) {
4047 struct leave_vm_options leave_options;
4048 char mailbox[AST_MAX_EXTENSION * 2 + 2];
4049 snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
4051 /* Send VoiceMail */
4052 memset(&leave_options, 0, sizeof(leave_options));
4053 leave_options.record_gain = record_gain;
4054 cmd = leave_voicemail(chan, mailbox, &leave_options);
4055 } else {
4057 /* Forward VoiceMail */
4058 long duration = 0;
4060 RETRIEVE(dir, curmsg);
4061 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, vms);
4062 if (!cmd) {
4063 AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
4064 #ifdef IMAP_STORAGE
4065 /* Need to get message content */
4066 if(option_debug > 2)
4067 ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
4068 if (vms->msgArray[vms->curmsg] == 0) {
4069 ast_log (LOG_WARNING,"Trying to access unknown message\n");
4070 return -1;
4073 /* This will only work for new messages... */
4074 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
4075 /* empty string means no valid header */
4076 if (ast_strlen_zero(header_content)) {
4077 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
4078 return -1;
4080 /* Get header info needed by sendmail */
4081 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:");
4082 if (temp)
4083 duration = atoi(temp);
4084 else
4085 duration = 0;
4087 /* Attach only the first format */
4088 fmt = ast_strdupa(fmt);
4089 if (fmt) {
4090 stringp = fmt;
4091 strsep(&stringp, "|");
4092 } else {
4093 ast_log (LOG_ERROR,"audio format not set. Default to WAV\n");
4094 fmt = "WAV";
4096 if (!strcasecmp(fmt, "wav49"))
4097 fmt = "WAV";
4098 if(option_debug > 2)
4099 ast_log (LOG_DEBUG,"**** format set to %s, vmfmts set to %s\n",fmt,vmfmts);
4100 /* ast_copy_string(fmt, vmfmts, sizeof(fmt));*/
4101 /* if (!ast_strlen_zero(fmt)) { */
4102 snprintf(todir, sizeof(todir), "%s%s/%s/tmp", VM_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
4103 make_gsm_file(vms->fn, vms->imapuser, todir, vms->curmsg);
4104 if(option_debug > 2)
4105 ast_log (LOG_DEBUG,"Before mail_fetchstructure, message number is %ld, filename is:%s\n",vms->msgArray[vms->curmsg], vms->fn);
4106 /*mail_fetchstructure (mailstream, vmArray[0], &body); */
4107 mail_fetchstructure (vms->mailstream, vms->msgArray[vms->curmsg], &body);
4108 save_body(body,vms,"3","gsm");
4109 /* should not assume "fmt" here! */
4110 save_body(body,vms,"2",fmt);
4112 STORE(todir, vmtmp->mailbox, vmtmp->context, vms->curmsg, chan, vmtmp, fmt, duration, vms);
4114 char *myserveremail = serveremail;
4115 if (!ast_strlen_zero(vmtmp->serveremail))
4116 myserveremail = vmtmp->serveremail;
4117 int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
4118 attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
4119 /* NULL category for IMAP storage */
4120 sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), vms->fn, fmt, duration, attach_user_voicemail, chan, NULL);
4121 #else
4122 copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt);
4123 #endif
4124 saved_messages++;
4125 AST_LIST_REMOVE_CURRENT(&extensions, list);
4126 free_user(vmtmp);
4127 if (res)
4128 break;
4130 AST_LIST_TRAVERSE_SAFE_END;
4131 if (saved_messages > 0) {
4132 /* give confirmation that the message was saved */
4133 /* commented out since we can't forward batches yet
4134 if (saved_messages == 1)
4135 res = ast_play_and_wait(chan, "vm-message");
4136 else
4137 res = ast_play_and_wait(chan, "vm-messages");
4138 if (!res)
4139 res = ast_play_and_wait(chan, "vm-saved"); */
4140 res = ast_play_and_wait(chan, "vm-msgsaved");
4144 return res ? res : cmd;
4147 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
4149 int res;
4150 if ((res = ast_stream_and_wait(chan, file, chan->language, AST_DIGIT_ANY)) < 0)
4151 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
4152 return res;
4155 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
4157 return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", "2", skipms);
4160 static int play_message_category(struct ast_channel *chan, const char *category)
4162 int res = 0;
4164 if (!ast_strlen_zero(category))
4165 res = ast_play_and_wait(chan, category);
4167 if (res) {
4168 ast_log(LOG_WARNING, "No sound file for category '%s' was found.\n", category);
4169 res = 0;
4172 return res;
4175 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
4177 int res = 0;
4178 struct vm_zone *the_zone = NULL;
4179 time_t t;
4181 if (ast_get_time_t(origtime, &t, 0, NULL)) {
4182 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
4183 return 0;
4186 /* Does this user have a timezone specified? */
4187 if (!ast_strlen_zero(vmu->zonetag)) {
4188 /* Find the zone in the list */
4189 struct vm_zone *z;
4190 AST_LIST_LOCK(&zones);
4191 AST_LIST_TRAVERSE(&zones, z, list) {
4192 if (!strcmp(z->name, vmu->zonetag)) {
4193 the_zone = z;
4194 break;
4197 AST_LIST_UNLOCK(&zones);
4200 /* No internal variable parsing for now, so we'll comment it out for the time being */
4201 #if 0
4202 /* Set the DIFF_* variables */
4203 localtime_r(&t, &time_now);
4204 tv_now = ast_tvnow();
4205 tnow = tv_now.tv_sec;
4206 localtime_r(&tnow,&time_then);
4208 /* Day difference */
4209 if (time_now.tm_year == time_then.tm_year)
4210 snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
4211 else
4212 snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
4213 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
4215 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
4216 #endif
4217 if (the_zone)
4218 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
4219 else if (!strcasecmp(chan->language,"pl")) /* POLISH syntax */
4220 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
4221 else if (!strcasecmp(chan->language,"se")) /* SWEDISH syntax */
4222 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
4223 else if (!strcasecmp(chan->language,"no")) /* NORWEGIAN syntax */
4224 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
4225 else if (!strcasecmp(chan->language,"de")) /* GERMAN syntax */
4226 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
4227 else if (!strcasecmp(chan->language,"nl")) /* DUTCH syntax */
4228 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
4229 else if (!strcasecmp(chan->language,"it")) /* ITALIAN syntax */
4230 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);
4231 else if (!strcasecmp(chan->language,"gr"))
4232 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q H 'digits/kai' M ", NULL);
4233 else
4234 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
4235 #if 0
4236 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
4237 #endif
4238 return res;
4243 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
4245 int res = 0;
4246 int i;
4247 char *callerid, *name;
4248 char prefile[256]="";
4251 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
4252 /* 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 */
4253 if ((cid == NULL)||(context == NULL))
4254 return res;
4256 /* Strip off caller ID number from name */
4257 ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
4258 ast_callerid_parse(cid, &name, &callerid);
4259 if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
4260 /* Check for internal contexts and only */
4261 /* say extension when the call didn't come from an internal context in the list */
4262 for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
4263 ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
4264 if ((strcmp(cidinternalcontexts[i], context) == 0))
4265 break;
4267 if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
4268 if (!res) {
4269 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
4270 if (!ast_strlen_zero(prefile)) {
4271 /* See if we can find a recorded name for this person instead of their extension number */
4272 if (ast_fileexists(prefile, NULL, NULL) > 0) {
4273 if (option_verbose > 2)
4274 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
4275 if (!callback)
4276 res = wait_file2(chan, vms, "vm-from");
4277 res = ast_stream_and_wait(chan, prefile, chan->language, "");
4278 } else {
4279 if (option_verbose > 2)
4280 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
4281 /* BB: Say "from extension" as one saying to sound smoother */
4282 if (!callback)
4283 res = wait_file2(chan, vms, "vm-from-extension");
4284 res = ast_say_digit_str(chan, callerid, "", chan->language);
4290 else if (!res){
4291 ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
4292 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
4293 if (!callback)
4294 res = wait_file2(chan, vms, "vm-from-phonenumber");
4295 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
4297 } else {
4298 /* Number unknown */
4299 ast_log(LOG_DEBUG, "VM-CID: From an unknown number\n");
4300 /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
4301 res = wait_file2(chan, vms, "vm-unknown-caller");
4303 return res;
4306 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
4308 int res = 0;
4309 int durationm;
4310 int durations;
4311 /* Verify that we have a duration for the message */
4312 if (duration == NULL)
4313 return res;
4315 /* Convert from seconds to minutes */
4316 durations=atoi(duration);
4317 durationm=(durations / 60);
4319 ast_log(LOG_DEBUG, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
4321 if ((!res) && (durationm >= minduration)) {
4322 res = wait_file2(chan, vms, "vm-duration");
4324 /* POLISH syntax */
4325 if (!strcasecmp(chan->language, "pl")) {
4326 div_t num = div(durationm, 10);
4328 if (durationm == 1) {
4329 res = ast_play_and_wait(chan, "digits/1z");
4330 res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
4331 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
4332 if (num.rem == 2) {
4333 if (!num.quot) {
4334 res = ast_play_and_wait(chan, "digits/2-ie");
4335 } else {
4336 res = say_and_wait(chan, durationm - 2 , chan->language);
4337 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
4339 } else {
4340 res = say_and_wait(chan, durationm, chan->language);
4342 res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
4343 } else {
4344 res = say_and_wait(chan, durationm, chan->language);
4345 res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
4347 /* DEFAULT syntax */
4348 } else {
4349 res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
4350 res = wait_file2(chan, vms, "vm-minutes");
4353 return res;
4356 #ifdef IMAP_STORAGE
4357 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
4359 BODY *body;
4360 char *header_content;
4361 char cid[256];
4362 char context[256];
4363 char origtime[32];
4364 char duration[16];
4365 char category[32];
4366 char todir[256];
4367 int res = 0;
4368 char *temp;
4370 vms->starting = 0;
4371 if(option_debug > 2)
4372 ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
4373 if (vms->msgArray[vms->curmsg] == 0) {
4374 ast_log (LOG_WARNING,"Trying to access unknown message\n");
4375 return -1;
4378 /* This will only work for new messages... */
4379 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
4380 /* empty string means no valid header */
4381 if (ast_strlen_zero(header_content)) {
4382 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
4383 return -1;
4385 snprintf(todir, sizeof(todir), "%s%s/%s/tmp", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
4386 make_gsm_file(vms->fn, vms->imapuser, todir, vms->curmsg);
4388 mail_fetchstructure (vms->mailstream,vms->msgArray[vms->curmsg],&body);
4389 save_body(body,vms,"3","gsm");
4391 adsi_message(chan, vms);
4392 if (!vms->curmsg)
4393 res = wait_file2(chan, vms, "vm-first"); /* "First" */
4394 else if (vms->curmsg == vms->lastmsg)
4395 res = wait_file2(chan, vms, "vm-last"); /* "last" */
4396 if (!res) {
4397 res = wait_file2(chan, vms, "vm-message"); /* "message" */
4398 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
4399 if (!res)
4400 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
4404 /* Get info from headers!! */
4405 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:");
4407 if (temp)
4408 ast_copy_string(cid, temp, sizeof(cid));
4409 else
4410 cid[0] = '\0';
4412 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Context:");
4414 if (temp)
4415 ast_copy_string(context, temp, sizeof(context));
4416 else
4417 context[0] = '\0';
4419 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:");
4421 if (temp)
4422 ast_copy_string(origtime, temp, sizeof(origtime));
4423 else
4424 origtime[0] = '\0';
4426 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:");
4428 if (temp)
4429 ast_copy_string(duration,temp, sizeof(duration));
4430 else
4431 duration[0] = '\0';
4433 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Category:");
4435 if (temp)
4436 ast_copy_string(category,temp, sizeof(category));
4437 else
4438 category[0] = '\0';
4440 /*if (!strncasecmp("macro",context,5)) Macro names in contexts are useless for our needs */
4441 /* context = ast_variable_retrieve(msg_cfg, "message","macrocontext"); */
4442 if (res == '1')
4443 res = 0;
4445 if ((!res) && !ast_strlen_zero(category)) {
4446 res = play_message_category(chan, category);
4449 if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)) && origtime[0] != '\0')
4450 res = play_message_datetime(chan, vmu, origtime, "IMAP_STORAGE");
4451 if ((!res) && (ast_test_flag(vmu, VM_SAYCID)) && cid[0] !='\0' && context[0] !='\0')
4452 res = play_message_callerid(chan, vms, cid, context, 0);
4454 if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)) && duration[0] != '\0')
4455 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
4457 /* Allow pressing '1' to skip envelope / callerid */
4458 /* if (res == '1')
4459 res = 0;
4461 /*ast_config_destroy(msg_cfg);*/
4462 res = 0;
4464 if (!res) {
4465 vms->heard[vms->curmsg] = 1;
4466 res = wait_file(chan, vms, vms->fn);
4468 DISPOSE(vms->curdir, vms->curmsg);
4469 DELETE(0, 0, vms->fn);
4470 return res;
4472 #else
4473 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
4475 int res = 0;
4476 char filename[256], *cid;
4477 const char *origtime, *context, *category, *duration;
4478 struct ast_config *msg_cfg;
4480 vms->starting = 0;
4481 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
4482 adsi_message(chan, vms);
4483 if (!vms->curmsg)
4484 res = wait_file2(chan, vms, "vm-first"); /* "First" */
4485 else if (vms->curmsg == vms->lastmsg)
4486 res = wait_file2(chan, vms, "vm-last"); /* "last" */
4487 if (!res) {
4488 /* POLISH syntax */
4489 if (!strcasecmp(chan->language, "pl")) {
4490 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
4491 int ten, one;
4492 char nextmsg[256];
4493 ten = (vms->curmsg + 1) / 10;
4494 one = (vms->curmsg + 1) % 10;
4496 if (vms->curmsg < 20) {
4497 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
4498 res = wait_file2(chan, vms, nextmsg);
4499 } else {
4500 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
4501 res = wait_file2(chan, vms, nextmsg);
4502 if (one > 0) {
4503 if (!res) {
4504 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
4505 res = wait_file2(chan, vms, nextmsg);
4510 if (!res)
4511 res = wait_file2(chan, vms, "vm-message");
4512 } else {
4513 if (!strcasecmp(chan->language, "se")) /* SWEDISH syntax */
4514 res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
4515 else /* DEFAULT syntax */
4516 res = wait_file2(chan, vms, "vm-message");
4517 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
4518 if (!res)
4519 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
4524 /* Retrieve info from VM attribute file */
4525 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
4526 snprintf(filename, sizeof(filename), "%s.txt", vms->fn2);
4527 RETRIEVE(vms->curdir, vms->curmsg);
4528 msg_cfg = ast_config_load(filename);
4529 if (!msg_cfg) {
4530 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
4531 return 0;
4534 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
4535 ast_log(LOG_WARNING, "No origtime?!\n");
4536 DISPOSE(vms->curdir, vms->curmsg);
4537 ast_config_destroy(msg_cfg);
4538 return 0;
4541 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
4542 duration = ast_variable_retrieve(msg_cfg, "message", "duration");
4543 category = ast_variable_retrieve(msg_cfg, "message", "category");
4545 context = ast_variable_retrieve(msg_cfg, "message", "context");
4546 if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
4547 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
4548 if (!res)
4549 res = play_message_category(chan, category);
4550 if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
4551 res = play_message_datetime(chan, vmu, origtime, filename);
4552 if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
4553 res = play_message_callerid(chan, vms, cid, context, 0);
4554 if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
4555 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
4556 /* Allow pressing '1' to skip envelope / callerid */
4557 if (res == '1')
4558 res = 0;
4559 ast_config_destroy(msg_cfg);
4561 if (!res) {
4562 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
4563 vms->heard[vms->curmsg] = 1;
4564 res = wait_file(chan, vms, vms->fn);
4566 DISPOSE(vms->curdir, vms->curmsg);
4567 return res;
4569 #endif
4571 #ifdef IMAP_STORAGE
4572 static void imap_mailbox_name(char *spec, struct vm_state *vms, int box, int use_folder)
4574 char tmp[256];
4576 if (box == 1) {
4577 ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
4578 sprintf(vms->vmbox, "vm-%s", mbox(1));
4579 } else {
4580 ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
4581 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
4584 if (strlen(authuser) > 0) {
4585 snprintf(tmp, sizeof(tmp), "{%s:%s/imap/authuser=%s/%s/user=%s}",imapserver,imapport,authuser,imapflags,vms->imapuser);
4586 } else {
4587 snprintf(tmp, sizeof(tmp), "{%s:%s/imap/%s/user=%s}",imapserver,imapport,imapflags,vms->imapuser);
4589 if(box == 0 || box == 1)
4590 sprintf(spec, "%s%s", tmp, use_folder? imapfolder: "INBOX");
4591 else
4592 sprintf(spec, "%s%s%c%s", tmp, imapfolder, delimiter, mbox(box));
4595 static int init_mailstream(struct vm_state *vms, int box)
4597 MAILSTREAM *stream = NIL;
4598 long debug;
4599 char tmp[255];
4601 if (!vms) {
4602 ast_log (LOG_ERROR,"vm_state is NULL!\n");
4603 return -1;
4605 if(option_debug > 2)
4606 ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
4607 if (vms->mailstream == NIL || !vms->mailstream) {
4608 ast_log (LOG_DEBUG,"mailstream not set.\n");
4609 } else {
4610 stream = vms->mailstream;
4612 /* debug = T; user wants protocol telemetry? */
4613 debug = NIL; /* NO protocol telemetry? */
4615 if (delimiter == '\0') { /* did not probe the server yet */
4616 char *cp;
4617 #include "linkage.c"
4618 /* Connect to INBOX first to get folders delimiter */
4619 imap_mailbox_name(tmp, vms, 0, 0);
4620 stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
4621 if (stream == NIL) {
4622 ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
4623 return NIL;
4625 get_mailbox_delimiter(stream);
4626 /* update delimiter in imapfolder */
4627 for(cp = imapfolder; *cp; cp++)
4628 if(*cp == '/')
4629 *cp = delimiter;
4631 /* Now connect to the target folder */
4632 imap_mailbox_name(tmp, vms, box, 1);
4633 if(option_debug > 2)
4634 ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
4635 vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
4636 if (vms->mailstream == NIL) {
4637 return -1;
4638 } else {
4639 return 0;
4643 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
4645 SEARCHPGM *pgm;
4646 SEARCHHEADER *hdr;
4647 int ret;
4648 char dbox[256];
4650 ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
4651 if(option_debug > 2)
4652 ast_log(LOG_DEBUG,"Before init_mailstream, user is %s\n",vmu->imapuser);
4653 ret = init_mailstream(vms, box);
4654 if (ret != 0 || !vms->mailstream) {
4655 ast_log (LOG_ERROR,"Could not initialize mailstream\n");
4656 return -1;
4659 /* Check Quota (here for now to test) */
4660 mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
4661 imap_mailbox_name(dbox, vms, box, 1);
4662 imap_getquotaroot(vms->mailstream, dbox);
4664 pgm = mail_newsearchpgm();
4666 /* Check IMAP folder for Asterisk messages only... */
4667 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", vmu->mailbox);
4668 pgm->header = hdr;
4669 pgm->deleted = 0;
4670 pgm->undeleted = 1;
4672 /* if box = 0, check for new, if box = 1, check for read */
4673 if (box == 0) {
4674 pgm->unseen = 1;
4675 pgm->seen = 0;
4676 } else if (box == 1) {
4677 pgm->seen = 1;
4678 pgm->unseen = 0;
4681 vms->vmArrayIndex = 0;
4682 if(option_debug > 2)
4683 ast_log(LOG_DEBUG,"Before mail_search_full, user is %s\n",vmu->imapuser);
4684 mail_search_full (vms->mailstream, NULL, pgm, NIL);
4687 vms->lastmsg = vms->vmArrayIndex - 1;
4689 mail_free_searchpgm(&pgm);
4690 return 0;
4692 #else
4693 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
4695 int res = 0;
4696 int count_msg, last_msg;
4698 ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
4700 /* Rename the member vmbox HERE so that we don't try to return before
4701 * we know what's going on.
4703 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
4705 /* Faster to make the directory than to check if it exists. */
4706 create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
4708 count_msg = count_messages(vmu, vms->curdir);
4709 if (count_msg < 0)
4710 return count_msg;
4711 else
4712 vms->lastmsg = count_msg - 1;
4715 The following test is needed in case sequencing gets messed up.
4716 There appears to be more than one way to mess up sequence, so
4717 we will not try to find all of the root causes--just fix it when
4718 detected.
4721 last_msg = last_message_index(vmu, vms->curdir);
4722 if (last_msg < 0)
4723 return last_msg;
4724 else if (vms->lastmsg != last_msg)
4726 ast_log(LOG_NOTICE, "Resequencing Mailbox: %s\n", vms->curdir);
4727 res = resequence_mailbox(vmu, vms->curdir);
4728 if (res)
4729 return res;
4732 return 0;
4734 #endif
4736 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
4738 int x = 0;
4739 #ifndef IMAP_STORAGE
4740 int res = 0, nummsg;
4741 #endif
4743 if (vms->lastmsg <= -1)
4744 goto done;
4746 vms->curmsg = -1;
4747 #ifndef IMAP_STORAGE
4748 /* Get the deleted messages fixed */
4749 if (vm_lock_path(vms->curdir))
4750 return ERROR_LOCK_PATH;
4752 for (x = 0; x < vmu->maxmsg; x++) {
4753 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
4754 /* Save this message. It's not in INBOX or hasn't been heard */
4755 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
4756 if (!EXISTS(vms->curdir, x, vms->fn, NULL))
4757 break;
4758 vms->curmsg++;
4759 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
4760 if (strcmp(vms->fn, vms->fn2)) {
4761 RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
4763 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
4764 /* Move to old folder before deleting */
4765 res = save_to_folder(vmu, vms, x, 1);
4766 if (res == ERROR_LOCK_PATH) {
4767 /* If save failed do not delete the message */
4768 vms->deleted[x] = 0;
4769 vms->heard[x] = 0;
4770 --x;
4775 /* Delete ALL remaining messages */
4776 nummsg = x - 1;
4777 for (x = vms->curmsg + 1; x <= nummsg; x++) {
4778 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
4779 if (EXISTS(vms->curdir, x, vms->fn, NULL))
4780 DELETE(vms->curdir, x, vms->fn);
4782 ast_unlock_path(vms->curdir);
4783 #else
4784 for (x=0;x < vmu->maxmsg;x++) {
4785 if (vms->deleted[x]) {
4786 if(option_debug > 2)
4787 ast_log(LOG_DEBUG,"IMAP delete of %d\n",x);
4788 IMAP_DELETE(vms->curdir, x, vms->fn, vms);
4791 #endif
4793 done:
4794 if (vms->deleted)
4795 memset(vms->deleted, 0, vmu->maxmsg * sizeof(int));
4796 if (vms->heard)
4797 memset(vms->heard, 0, vmu->maxmsg * sizeof(int));
4799 return 0;
4802 /* In Greek even though we CAN use a syntax like "friends messages"
4803 * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
4804 * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
4805 * syntax for the above three categories which is more elegant.
4808 static int vm_play_folder_name_gr(struct ast_channel *chan, char *mbox)
4810 int cmd;
4811 char *buf;
4813 buf = alloca(strlen(mbox)+2);
4814 strcpy(buf, mbox);
4815 strcat(buf,"s");
4817 if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")){
4818 cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
4819 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
4820 } else {
4821 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
4822 return cmd ? cmd : ast_play_and_wait(chan, mbox); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
4826 static int vm_play_folder_name_pl(struct ast_channel *chan, char *mbox)
4828 int cmd;
4830 if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")) {
4831 if (!strcasecmp(mbox, "vm-INBOX"))
4832 cmd = ast_play_and_wait(chan, "vm-new-e");
4833 else
4834 cmd = ast_play_and_wait(chan, "vm-old-e");
4835 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
4836 } else {
4837 cmd = ast_play_and_wait(chan, "vm-messages");
4838 return cmd ? cmd : ast_play_and_wait(chan, mbox);
4842 static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
4844 int cmd;
4846 if (!strcasecmp(chan->language, "it") || !strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "fr") || !strcasecmp(chan->language, "pt")) { /* Italian, Spanish, French or Portuguese syntax */
4847 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
4848 return cmd ? cmd : ast_play_and_wait(chan, mbox);
4849 } else if (!strcasecmp(chan->language, "gr")){
4850 return vm_play_folder_name_gr(chan, mbox);
4851 } else if (!strcasecmp(chan->language, "pl")){
4852 return vm_play_folder_name_pl(chan, mbox);
4853 } else { /* Default English */
4854 cmd = ast_play_and_wait(chan, mbox);
4855 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
4859 /* GREEK SYNTAX
4860 In greek the plural for old/new is
4861 different so we need the following files
4862 We also need vm-denExeteMynhmata because
4863 this syntax is different.
4865 -> vm-Olds.wav : "Palia"
4866 -> vm-INBOXs.wav : "Nea"
4867 -> vm-denExeteMynhmata : "den exete mynhmata"
4871 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
4873 int res = 0;
4875 if (vms->newmessages) {
4876 res = ast_play_and_wait(chan, "vm-youhave");
4877 if (!res)
4878 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
4879 if (!res) {
4880 if ((vms->newmessages == 1)) {
4881 res = ast_play_and_wait(chan, "vm-INBOX");
4882 if (!res)
4883 res = ast_play_and_wait(chan, "vm-message");
4884 } else {
4885 res = ast_play_and_wait(chan, "vm-INBOXs");
4886 if (!res)
4887 res = ast_play_and_wait(chan, "vm-messages");
4890 } else if (vms->oldmessages){
4891 res = ast_play_and_wait(chan, "vm-youhave");
4892 if (!res)
4893 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
4894 if ((vms->oldmessages == 1)){
4895 res = ast_play_and_wait(chan, "vm-Old");
4896 if (!res)
4897 res = ast_play_and_wait(chan, "vm-message");
4898 } else {
4899 res = ast_play_and_wait(chan, "vm-Olds");
4900 if (!res)
4901 res = ast_play_and_wait(chan, "vm-messages");
4903 } else if (!vms->oldmessages && !vms->newmessages)
4904 res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
4905 return res;
4908 /* Default English syntax */
4909 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
4911 int res;
4913 /* Introduce messages they have */
4914 res = ast_play_and_wait(chan, "vm-youhave");
4915 if (!res) {
4916 if (vms->newmessages) {
4917 res = say_and_wait(chan, vms->newmessages, chan->language);
4918 if (!res)
4919 res = ast_play_and_wait(chan, "vm-INBOX");
4920 if (vms->oldmessages && !res)
4921 res = ast_play_and_wait(chan, "vm-and");
4922 else if (!res) {
4923 if ((vms->newmessages == 1))
4924 res = ast_play_and_wait(chan, "vm-message");
4925 else
4926 res = ast_play_and_wait(chan, "vm-messages");
4930 if (!res && vms->oldmessages) {
4931 res = say_and_wait(chan, vms->oldmessages, chan->language);
4932 if (!res)
4933 res = ast_play_and_wait(chan, "vm-Old");
4934 if (!res) {
4935 if (vms->oldmessages == 1)
4936 res = ast_play_and_wait(chan, "vm-message");
4937 else
4938 res = ast_play_and_wait(chan, "vm-messages");
4941 if (!res) {
4942 if (!vms->oldmessages && !vms->newmessages) {
4943 res = ast_play_and_wait(chan, "vm-no");
4944 if (!res)
4945 res = ast_play_and_wait(chan, "vm-messages");
4949 return res;
4952 /* ITALIAN syntax */
4953 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
4955 /* Introduce messages they have */
4956 int res;
4957 if (!vms->oldmessages && !vms->newmessages)
4958 res = ast_play_and_wait(chan, "vm-no") ||
4959 ast_play_and_wait(chan, "vm-message");
4960 else
4961 res = ast_play_and_wait(chan, "vm-youhave");
4962 if (!res && vms->newmessages) {
4963 res = (vms->newmessages == 1) ?
4964 ast_play_and_wait(chan, "digits/un") ||
4965 ast_play_and_wait(chan, "vm-nuovo") ||
4966 ast_play_and_wait(chan, "vm-message") :
4967 /* 2 or more new messages */
4968 say_and_wait(chan, vms->newmessages, chan->language) ||
4969 ast_play_and_wait(chan, "vm-nuovi") ||
4970 ast_play_and_wait(chan, "vm-messages");
4971 if (!res && vms->oldmessages)
4972 res = ast_play_and_wait(chan, "vm-and");
4974 if (!res && vms->oldmessages) {
4975 res = (vms->oldmessages == 1) ?
4976 ast_play_and_wait(chan, "digits/un") ||
4977 ast_play_and_wait(chan, "vm-vecchio") ||
4978 ast_play_and_wait(chan, "vm-message") :
4979 /* 2 or more old messages */
4980 say_and_wait(chan, vms->oldmessages, chan->language) ||
4981 ast_play_and_wait(chan, "vm-vecchi") ||
4982 ast_play_and_wait(chan, "vm-messages");
4984 return res ? -1 : 0;
4987 /* POLISH syntax */
4988 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
4990 /* Introduce messages they have */
4991 int res;
4992 div_t num;
4994 if (!vms->oldmessages && !vms->newmessages) {
4995 res = ast_play_and_wait(chan, "vm-no");
4996 res = res ? res : ast_play_and_wait(chan, "vm-messages");
4997 return res;
4998 } else {
4999 res = ast_play_and_wait(chan, "vm-youhave");
5002 if (vms->newmessages) {
5003 num = div(vms->newmessages, 10);
5004 if (vms->newmessages == 1) {
5005 res = ast_play_and_wait(chan, "digits/1-a");
5006 res = res ? res : ast_play_and_wait(chan, "vm-new-a");
5007 res = res ? res : ast_play_and_wait(chan, "vm-message");
5008 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
5009 if (num.rem == 2) {
5010 if (!num.quot) {
5011 res = ast_play_and_wait(chan, "digits/2-ie");
5012 } else {
5013 res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
5014 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
5016 } else {
5017 res = say_and_wait(chan, vms->newmessages, chan->language);
5019 res = res ? res : ast_play_and_wait(chan, "vm-new-e");
5020 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5021 } else {
5022 res = say_and_wait(chan, vms->newmessages, chan->language);
5023 res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
5024 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5026 if (!res && vms->oldmessages)
5027 res = ast_play_and_wait(chan, "vm-and");
5029 if (!res && vms->oldmessages) {
5030 num = div(vms->oldmessages, 10);
5031 if (vms->oldmessages == 1) {
5032 res = ast_play_and_wait(chan, "digits/1-a");
5033 res = res ? res : ast_play_and_wait(chan, "vm-old-a");
5034 res = res ? res : ast_play_and_wait(chan, "vm-message");
5035 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
5036 if (num.rem == 2) {
5037 if (!num.quot) {
5038 res = ast_play_and_wait(chan, "digits/2-ie");
5039 } else {
5040 res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
5041 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
5043 } else {
5044 res = say_and_wait(chan, vms->oldmessages, chan->language);
5046 res = res ? res : ast_play_and_wait(chan, "vm-old-e");
5047 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5048 } else {
5049 res = say_and_wait(chan, vms->oldmessages, chan->language);
5050 res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
5051 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5055 return res;
5058 /* SWEDISH syntax */
5059 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
5061 /* Introduce messages they have */
5062 int res;
5064 res = ast_play_and_wait(chan, "vm-youhave");
5065 if (res)
5066 return res;
5068 if (!vms->oldmessages && !vms->newmessages) {
5069 res = ast_play_and_wait(chan, "vm-no");
5070 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5071 return res;
5074 if (vms->newmessages) {
5075 if ((vms->newmessages == 1)) {
5076 res = ast_play_and_wait(chan, "digits/ett");
5077 res = res ? res : ast_play_and_wait(chan, "vm-nytt");
5078 res = res ? res : ast_play_and_wait(chan, "vm-message");
5079 } else {
5080 res = say_and_wait(chan, vms->newmessages, chan->language);
5081 res = res ? res : ast_play_and_wait(chan, "vm-nya");
5082 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5084 if (!res && vms->oldmessages)
5085 res = ast_play_and_wait(chan, "vm-and");
5087 if (!res && vms->oldmessages) {
5088 if (vms->oldmessages == 1) {
5089 res = ast_play_and_wait(chan, "digits/ett");
5090 res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
5091 res = res ? res : ast_play_and_wait(chan, "vm-message");
5092 } else {
5093 res = say_and_wait(chan, vms->oldmessages, chan->language);
5094 res = res ? res : ast_play_and_wait(chan, "vm-gamla");
5095 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5099 return res;
5102 /* NORWEGIAN syntax */
5103 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
5105 /* Introduce messages they have */
5106 int res;
5108 res = ast_play_and_wait(chan, "vm-youhave");
5109 if (res)
5110 return res;
5112 if (!vms->oldmessages && !vms->newmessages) {
5113 res = ast_play_and_wait(chan, "vm-no");
5114 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5115 return res;
5118 if (vms->newmessages) {
5119 if ((vms->newmessages == 1)) {
5120 res = ast_play_and_wait(chan, "digits/1");
5121 res = res ? res : ast_play_and_wait(chan, "vm-ny");
5122 res = res ? res : ast_play_and_wait(chan, "vm-message");
5123 } else {
5124 res = say_and_wait(chan, vms->newmessages, chan->language);
5125 res = res ? res : ast_play_and_wait(chan, "vm-nye");
5126 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5128 if (!res && vms->oldmessages)
5129 res = ast_play_and_wait(chan, "vm-and");
5131 if (!res && vms->oldmessages) {
5132 if (vms->oldmessages == 1) {
5133 res = ast_play_and_wait(chan, "digits/1");
5134 res = res ? res : ast_play_and_wait(chan, "vm-gamel");
5135 res = res ? res : ast_play_and_wait(chan, "vm-message");
5136 } else {
5137 res = say_and_wait(chan, vms->oldmessages, chan->language);
5138 res = res ? res : ast_play_and_wait(chan, "vm-gamle");
5139 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5143 return res;
5146 /* GERMAN syntax */
5147 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
5149 /* Introduce messages they have */
5150 int res;
5151 res = ast_play_and_wait(chan, "vm-youhave");
5152 if (!res) {
5153 if (vms->newmessages) {
5154 if ((vms->newmessages == 1))
5155 res = ast_play_and_wait(chan, "digits/1F");
5156 else
5157 res = say_and_wait(chan, vms->newmessages, chan->language);
5158 if (!res)
5159 res = ast_play_and_wait(chan, "vm-INBOX");
5160 if (vms->oldmessages && !res)
5161 res = ast_play_and_wait(chan, "vm-and");
5162 else if (!res) {
5163 if ((vms->newmessages == 1))
5164 res = ast_play_and_wait(chan, "vm-message");
5165 else
5166 res = ast_play_and_wait(chan, "vm-messages");
5170 if (!res && vms->oldmessages) {
5171 if (vms->oldmessages == 1)
5172 res = ast_play_and_wait(chan, "digits/1F");
5173 else
5174 res = say_and_wait(chan, vms->oldmessages, chan->language);
5175 if (!res)
5176 res = ast_play_and_wait(chan, "vm-Old");
5177 if (!res) {
5178 if (vms->oldmessages == 1)
5179 res = ast_play_and_wait(chan, "vm-message");
5180 else
5181 res = ast_play_and_wait(chan, "vm-messages");
5184 if (!res) {
5185 if (!vms->oldmessages && !vms->newmessages) {
5186 res = ast_play_and_wait(chan, "vm-no");
5187 if (!res)
5188 res = ast_play_and_wait(chan, "vm-messages");
5192 return res;
5195 /* SPANISH syntax */
5196 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
5198 /* Introduce messages they have */
5199 int res;
5200 if (!vms->oldmessages && !vms->newmessages) {
5201 res = ast_play_and_wait(chan, "vm-youhaveno");
5202 if (!res)
5203 res = ast_play_and_wait(chan, "vm-messages");
5204 } else {
5205 res = ast_play_and_wait(chan, "vm-youhave");
5207 if (!res) {
5208 if (vms->newmessages) {
5209 if (!res) {
5210 if ((vms->newmessages == 1)) {
5211 res = ast_play_and_wait(chan, "digits/1M");
5212 if (!res)
5213 res = ast_play_and_wait(chan, "vm-message");
5214 if (!res)
5215 res = ast_play_and_wait(chan, "vm-INBOXs");
5216 } else {
5217 res = say_and_wait(chan, vms->newmessages, chan->language);
5218 if (!res)
5219 res = ast_play_and_wait(chan, "vm-messages");
5220 if (!res)
5221 res = ast_play_and_wait(chan, "vm-INBOX");
5224 if (vms->oldmessages && !res)
5225 res = ast_play_and_wait(chan, "vm-and");
5227 if (vms->oldmessages) {
5228 if (!res) {
5229 if (vms->oldmessages == 1) {
5230 res = ast_play_and_wait(chan, "digits/1M");
5231 if (!res)
5232 res = ast_play_and_wait(chan, "vm-message");
5233 if (!res)
5234 res = ast_play_and_wait(chan, "vm-Olds");
5235 } else {
5236 res = say_and_wait(chan, vms->oldmessages, chan->language);
5237 if (!res)
5238 res = ast_play_and_wait(chan, "vm-messages");
5239 if (!res)
5240 res = ast_play_and_wait(chan, "vm-Old");
5245 return res;
5248 /* FRENCH syntax */
5249 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
5251 /* Introduce messages they have */
5252 int res;
5253 res = ast_play_and_wait(chan, "vm-youhave");
5254 if (!res) {
5255 if (vms->newmessages) {
5256 res = say_and_wait(chan, vms->newmessages, chan->language);
5257 if (!res)
5258 res = ast_play_and_wait(chan, "vm-INBOX");
5259 if (vms->oldmessages && !res)
5260 res = ast_play_and_wait(chan, "vm-and");
5261 else if (!res) {
5262 if ((vms->newmessages == 1))
5263 res = ast_play_and_wait(chan, "vm-message");
5264 else
5265 res = ast_play_and_wait(chan, "vm-messages");
5269 if (!res && vms->oldmessages) {
5270 res = say_and_wait(chan, vms->oldmessages, chan->language);
5271 if (!res) {
5272 if (vms->oldmessages == 1)
5273 res = ast_play_and_wait(chan, "vm-message");
5274 else
5275 res = ast_play_and_wait(chan, "vm-messages");
5277 if (!res)
5278 res = ast_play_and_wait(chan, "vm-Old");
5280 if (!res) {
5281 if (!vms->oldmessages && !vms->newmessages) {
5282 res = ast_play_and_wait(chan, "vm-no");
5283 if (!res)
5284 res = ast_play_and_wait(chan, "vm-messages");
5288 return res;
5291 /* DUTCH syntax */
5292 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
5294 /* Introduce messages they have */
5295 int res;
5296 res = ast_play_and_wait(chan, "vm-youhave");
5297 if (!res) {
5298 if (vms->newmessages) {
5299 res = say_and_wait(chan, vms->newmessages, chan->language);
5300 if (!res) {
5301 if (vms->newmessages == 1)
5302 res = ast_play_and_wait(chan, "vm-INBOXs");
5303 else
5304 res = ast_play_and_wait(chan, "vm-INBOX");
5306 if (vms->oldmessages && !res)
5307 res = ast_play_and_wait(chan, "vm-and");
5308 else if (!res) {
5309 if ((vms->newmessages == 1))
5310 res = ast_play_and_wait(chan, "vm-message");
5311 else
5312 res = ast_play_and_wait(chan, "vm-messages");
5316 if (!res && vms->oldmessages) {
5317 res = say_and_wait(chan, vms->oldmessages, chan->language);
5318 if (!res) {
5319 if (vms->oldmessages == 1)
5320 res = ast_play_and_wait(chan, "vm-Olds");
5321 else
5322 res = ast_play_and_wait(chan, "vm-Old");
5324 if (!res) {
5325 if (vms->oldmessages == 1)
5326 res = ast_play_and_wait(chan, "vm-message");
5327 else
5328 res = ast_play_and_wait(chan, "vm-messages");
5331 if (!res) {
5332 if (!vms->oldmessages && !vms->newmessages) {
5333 res = ast_play_and_wait(chan, "vm-no");
5334 if (!res)
5335 res = ast_play_and_wait(chan, "vm-messages");
5339 return res;
5342 /* PORTUGUESE syntax */
5343 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
5345 /* Introduce messages they have */
5346 int res;
5347 res = ast_play_and_wait(chan, "vm-youhave");
5348 if (!res) {
5349 if (vms->newmessages) {
5350 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
5351 if (!res) {
5352 if ((vms->newmessages == 1)) {
5353 res = ast_play_and_wait(chan, "vm-message");
5354 if (!res)
5355 res = ast_play_and_wait(chan, "vm-INBOXs");
5356 } else {
5357 res = ast_play_and_wait(chan, "vm-messages");
5358 if (!res)
5359 res = ast_play_and_wait(chan, "vm-INBOX");
5362 if (vms->oldmessages && !res)
5363 res = ast_play_and_wait(chan, "vm-and");
5365 if (!res && vms->oldmessages) {
5366 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
5367 if (!res) {
5368 if (vms->oldmessages == 1) {
5369 res = ast_play_and_wait(chan, "vm-message");
5370 if (!res)
5371 res = ast_play_and_wait(chan, "vm-Olds");
5372 } else {
5373 res = ast_play_and_wait(chan, "vm-messages");
5374 if (!res)
5375 res = ast_play_and_wait(chan, "vm-Old");
5379 if (!res) {
5380 if (!vms->oldmessages && !vms->newmessages) {
5381 res = ast_play_and_wait(chan, "vm-no");
5382 if (!res)
5383 res = ast_play_and_wait(chan, "vm-messages");
5387 return res;
5391 /* CZECH syntax */
5392 /* in czech there must be declension of word new and message
5393 * czech : english : czech : english
5394 * --------------------------------------------------------
5395 * vm-youhave : you have
5396 * vm-novou : one new : vm-zpravu : message
5397 * vm-nove : 2-4 new : vm-zpravy : messages
5398 * vm-novych : 5-infinite new : vm-zprav : messages
5399 * vm-starou : one old
5400 * vm-stare : 2-4 old
5401 * vm-starych : 5-infinite old
5402 * jednu : one - falling 4.
5403 * vm-no : no ( no messages )
5406 static int vm_intro_cz(struct ast_channel *chan,struct vm_state *vms)
5408 int res;
5409 res = ast_play_and_wait(chan, "vm-youhave");
5410 if (!res) {
5411 if (vms->newmessages) {
5412 if (vms->newmessages == 1) {
5413 res = ast_play_and_wait(chan, "digits/jednu");
5414 } else {
5415 res = say_and_wait(chan, vms->newmessages, chan->language);
5417 if (!res) {
5418 if ((vms->newmessages == 1))
5419 res = ast_play_and_wait(chan, "vm-novou");
5420 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
5421 res = ast_play_and_wait(chan, "vm-nove");
5422 if (vms->newmessages > 4)
5423 res = ast_play_and_wait(chan, "vm-novych");
5425 if (vms->oldmessages && !res)
5426 res = ast_play_and_wait(chan, "vm-and");
5427 else if (!res) {
5428 if ((vms->newmessages == 1))
5429 res = ast_play_and_wait(chan, "vm-zpravu");
5430 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
5431 res = ast_play_and_wait(chan, "vm-zpravy");
5432 if (vms->newmessages > 4)
5433 res = ast_play_and_wait(chan, "vm-zprav");
5436 if (!res && vms->oldmessages) {
5437 res = say_and_wait(chan, vms->oldmessages, chan->language);
5438 if (!res) {
5439 if ((vms->oldmessages == 1))
5440 res = ast_play_and_wait(chan, "vm-starou");
5441 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
5442 res = ast_play_and_wait(chan, "vm-stare");
5443 if (vms->oldmessages > 4)
5444 res = ast_play_and_wait(chan, "vm-starych");
5446 if (!res) {
5447 if ((vms->oldmessages == 1))
5448 res = ast_play_and_wait(chan, "vm-zpravu");
5449 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
5450 res = ast_play_and_wait(chan, "vm-zpravy");
5451 if (vms->oldmessages > 4)
5452 res = ast_play_and_wait(chan, "vm-zprav");
5455 if (!res) {
5456 if (!vms->oldmessages && !vms->newmessages) {
5457 res = ast_play_and_wait(chan, "vm-no");
5458 if (!res)
5459 res = ast_play_and_wait(chan, "vm-zpravy");
5463 return res;
5466 static int get_lastdigits(int num)
5468 num %= 100;
5469 return (num < 20) ? num : num % 10;
5472 static int vm_intro_ru(struct ast_channel *chan,struct vm_state *vms)
5474 int res;
5475 int lastnum = 0;
5476 int dcnum;
5478 res = ast_play_and_wait(chan, "vm-youhave");
5479 if (!res && vms->newmessages) {
5480 lastnum = get_lastdigits(vms->newmessages);
5481 dcnum = vms->newmessages - lastnum;
5482 if (dcnum)
5483 res = say_and_wait(chan, dcnum, chan->language);
5484 if (!res && lastnum) {
5485 if (lastnum == 1)
5486 res = ast_play_and_wait(chan, "digits/ru/odno");
5487 else
5488 res = say_and_wait(chan, lastnum, chan->language);
5491 if (!res)
5492 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-novoe" : "vm-novyh");
5494 if (!res && vms->oldmessages)
5495 res = ast_play_and_wait(chan, "vm-and");
5498 if (!res && vms->oldmessages) {
5499 lastnum = get_lastdigits(vms->oldmessages);
5500 dcnum = vms->newmessages - lastnum;
5501 if (dcnum)
5502 res = say_and_wait(chan, dcnum, chan->language);
5503 if (!res && lastnum) {
5504 if (lastnum == 1)
5505 res = ast_play_and_wait(chan, "digits/ru/odno");
5506 else
5507 res = say_and_wait(chan, lastnum, chan->language);
5510 if (!res)
5511 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-staroe" : "vm-staryh");
5514 if (!res && !vms->newmessages && !vms->oldmessages) {
5515 lastnum = 0;
5516 res = ast_play_and_wait(chan, "vm-no");
5519 if (!res) {
5520 switch (lastnum) {
5521 case 1:
5522 res = ast_play_and_wait(chan, "vm-soobshenie");
5523 break;
5524 case 2:
5525 case 3:
5526 case 4:
5527 res = ast_play_and_wait(chan, "vm-soobsheniya");
5528 break;
5529 default:
5530 res = ast_play_and_wait(chan, "vm-soobsheniy");
5531 break;
5535 return res;
5539 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
5541 char prefile[256];
5543 /* Notify the user that the temp greeting is set and give them the option to remove it */
5544 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
5545 if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
5546 if (ast_fileexists(prefile, NULL, NULL) > 0)
5547 ast_play_and_wait(chan, "vm-tempgreetactive");
5550 /* Play voicemail intro - syntax is different for different languages */
5551 if (!strcasecmp(chan->language, "de")) { /* GERMAN syntax */
5552 return vm_intro_de(chan, vms);
5553 } else if (!strcasecmp(chan->language, "es")) { /* SPANISH syntax */
5554 return vm_intro_es(chan, vms);
5555 } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN syntax */
5556 return vm_intro_it(chan, vms);
5557 } else if (!strcasecmp(chan->language, "fr")) { /* FRENCH syntax */
5558 return vm_intro_fr(chan, vms);
5559 } else if (!strcasecmp(chan->language, "nl")) { /* DUTCH syntax */
5560 return vm_intro_nl(chan, vms);
5561 } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE syntax */
5562 return vm_intro_pt(chan, vms);
5563 } else if (!strcasecmp(chan->language, "cz")) { /* CZECH syntax */
5564 return vm_intro_cz(chan, vms);
5565 } else if (!strcasecmp(chan->language, "gr")) { /* GREEK syntax */
5566 return vm_intro_gr(chan, vms);
5567 } else if (!strcasecmp(chan->language, "pl")) { /* POLISH syntax */
5568 return vm_intro_pl(chan, vms);
5569 } else if (!strcasecmp(chan->language, "se")) { /* SWEDISH syntax */
5570 return vm_intro_se(chan, vms);
5571 } else if (!strcasecmp(chan->language, "no")) { /* NORWEGIAN syntax */
5572 return vm_intro_no(chan, vms);
5573 } else if (!strcasecmp(chan->language, "ru")) { /* RUSSIAN syntax */
5574 return vm_intro_ru(chan, vms);
5575 } else { /* Default to ENGLISH */
5576 return vm_intro_en(chan, vms);
5580 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
5582 int res = 0;
5583 /* Play instructions and wait for new command */
5584 while (!res) {
5585 if (vms->starting) {
5586 if (vms->lastmsg > -1) {
5587 res = ast_play_and_wait(chan, "vm-onefor");
5588 if (!res)
5589 res = vm_play_folder_name(chan, vms->vmbox);
5591 if (!res)
5592 res = ast_play_and_wait(chan, "vm-opts");
5593 } else {
5594 if (vms->curmsg)
5595 res = ast_play_and_wait(chan, "vm-prev");
5596 if (!res && !skipadvanced)
5597 res = ast_play_and_wait(chan, "vm-advopts");
5598 if (!res)
5599 res = ast_play_and_wait(chan, "vm-repeat");
5600 if (!res && (vms->curmsg != vms->lastmsg))
5601 res = ast_play_and_wait(chan, "vm-next");
5602 if (!res) {
5603 if (!vms->deleted[vms->curmsg])
5604 res = ast_play_and_wait(chan, "vm-delete");
5605 else
5606 res = ast_play_and_wait(chan, "vm-undelete");
5607 if (!res)
5608 res = ast_play_and_wait(chan, "vm-toforward");
5609 if (!res)
5610 res = ast_play_and_wait(chan, "vm-savemessage");
5613 if (!res)
5614 res = ast_play_and_wait(chan, "vm-helpexit");
5615 if (!res)
5616 res = ast_waitfordigit(chan, 6000);
5617 if (!res) {
5618 vms->repeats++;
5619 if (vms->repeats > 2) {
5620 res = 't';
5624 return res;
5627 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
5629 int cmd = 0;
5630 int duration = 0;
5631 int tries = 0;
5632 char newpassword[80] = "";
5633 char newpassword2[80] = "";
5634 char prefile[256]="";
5635 unsigned char buf[256];
5636 int bytes=0;
5638 if (ast_adsi_available(chan)) {
5639 bytes += adsi_logo(buf + bytes);
5640 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
5641 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
5642 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5643 bytes += ast_adsi_voice_mode(buf + bytes, 0);
5644 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5647 /* First, have the user change their password
5648 so they won't get here again */
5649 for (;;) {
5650 newpassword[1] = '\0';
5651 newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
5652 if (cmd == '#')
5653 newpassword[0] = '\0';
5654 if (cmd < 0 || cmd == 't' || cmd == '#')
5655 return cmd;
5656 cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
5657 if (cmd < 0 || cmd == 't' || cmd == '#')
5658 return cmd;
5659 newpassword2[1] = '\0';
5660 newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
5661 if (cmd == '#')
5662 newpassword2[0] = '\0';
5663 if (cmd < 0 || cmd == 't' || cmd == '#')
5664 return cmd;
5665 cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#");
5666 if (cmd < 0 || cmd == 't' || cmd == '#')
5667 return cmd;
5668 if (!strcmp(newpassword, newpassword2))
5669 break;
5670 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
5671 cmd = ast_play_and_wait(chan, "vm-mismatch");
5672 if (++tries == 3)
5673 return -1;
5675 if (ast_strlen_zero(ext_pass_cmd))
5676 vm_change_password(vmu,newpassword);
5677 else
5678 vm_change_password_shell(vmu,newpassword);
5679 ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
5680 cmd = ast_play_and_wait(chan,"vm-passchanged");
5682 /* If forcename is set, have the user record their name */
5683 if (ast_test_flag(vmu, VM_FORCENAME)) {
5684 snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
5685 cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
5686 if (cmd < 0 || cmd == 't' || cmd == '#')
5687 return cmd;
5690 /* If forcegreetings is set, have the user record their greetings */
5691 if (ast_test_flag(vmu, VM_FORCEGREET)) {
5692 snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
5693 cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
5694 if (cmd < 0 || cmd == 't' || cmd == '#')
5695 return cmd;
5696 snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
5697 cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
5698 if (cmd < 0 || cmd == 't' || cmd == '#')
5699 return cmd;
5702 return cmd;
5705 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
5707 int cmd = 0;
5708 int retries = 0;
5709 int duration = 0;
5710 char newpassword[80] = "";
5711 char newpassword2[80] = "";
5712 char prefile[256]="";
5713 unsigned char buf[256];
5714 int bytes=0;
5716 if (ast_adsi_available(chan))
5718 bytes += adsi_logo(buf + bytes);
5719 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
5720 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
5721 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5722 bytes += ast_adsi_voice_mode(buf + bytes, 0);
5723 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5725 while ((cmd >= 0) && (cmd != 't')) {
5726 if (cmd)
5727 retries = 0;
5728 switch (cmd) {
5729 case '1':
5730 snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
5731 cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
5732 break;
5733 case '2':
5734 snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
5735 cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
5736 break;
5737 case '3':
5738 snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
5739 cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
5740 break;
5741 case '4':
5742 cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
5743 break;
5744 case '5':
5745 if (vmu->password[0] == '-') {
5746 cmd = ast_play_and_wait(chan, "vm-no");
5747 break;
5749 newpassword[1] = '\0';
5750 newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
5751 if (cmd == '#')
5752 newpassword[0] = '\0';
5753 else {
5754 if (cmd < 0)
5755 break;
5756 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
5757 break;
5760 newpassword2[1] = '\0';
5761 newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
5762 if (cmd == '#')
5763 newpassword2[0] = '\0';
5764 else {
5765 if (cmd < 0)
5766 break;
5768 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
5769 break;
5772 if (strcmp(newpassword, newpassword2)) {
5773 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
5774 cmd = ast_play_and_wait(chan, "vm-mismatch");
5775 break;
5777 if (ast_strlen_zero(ext_pass_cmd))
5778 vm_change_password(vmu,newpassword);
5779 else
5780 vm_change_password_shell(vmu,newpassword);
5781 ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
5782 cmd = ast_play_and_wait(chan,"vm-passchanged");
5783 break;
5784 case '*':
5785 cmd = 't';
5786 break;
5787 default:
5788 cmd = ast_play_and_wait(chan,"vm-options");
5789 if (!cmd)
5790 cmd = ast_waitfordigit(chan,6000);
5791 if (!cmd)
5792 retries++;
5793 if (retries > 3)
5794 cmd = 't';
5797 if (cmd == 't')
5798 cmd = 0;
5799 return cmd;
5802 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
5804 int cmd = 0;
5805 int retries = 0;
5806 int duration = 0;
5807 char prefile[256]="";
5808 unsigned char buf[256];
5809 int bytes=0;
5811 if (ast_adsi_available(chan)) {
5812 bytes += adsi_logo(buf + bytes);
5813 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
5814 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
5815 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5816 bytes += ast_adsi_voice_mode(buf + bytes, 0);
5817 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5819 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
5820 while (cmd >= 0 && cmd != 't') {
5821 if (cmd)
5822 retries = 0;
5823 RETRIEVE(prefile, -1);
5824 if (ast_fileexists(prefile, NULL, NULL) <= 0) {
5825 play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
5826 cmd = 't';
5827 } else {
5828 switch (cmd) {
5829 case '1':
5830 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
5831 break;
5832 case '2':
5833 DELETE(prefile, -1, prefile);
5834 ast_play_and_wait(chan, "vm-tempremoved");
5835 cmd = 't';
5836 break;
5837 case '*':
5838 cmd = 't';
5839 break;
5840 default:
5841 cmd = ast_play_and_wait(chan,
5842 ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
5843 "vm-tempgreeting2" : "vm-tempgreeting");
5844 if (!cmd)
5845 cmd = ast_waitfordigit(chan,6000);
5846 if (!cmd)
5847 retries++;
5848 if (retries > 3)
5849 cmd = 't';
5852 DISPOSE(prefile, -1);
5854 if (cmd == 't')
5855 cmd = 0;
5856 return cmd;
5859 /* GREEK SYNTAX */
5861 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
5863 int cmd=0;
5865 if (vms->lastmsg > -1) {
5866 cmd = play_message(chan, vmu, vms);
5867 } else {
5868 cmd = ast_play_and_wait(chan, "vm-youhaveno");
5869 if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
5870 if (!cmd) {
5871 snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
5872 cmd = ast_play_and_wait(chan, vms->fn);
5874 if (!cmd)
5875 cmd = ast_play_and_wait(chan, "vm-messages");
5876 } else {
5877 if (!cmd)
5878 cmd = ast_play_and_wait(chan, "vm-messages");
5879 if (!cmd) {
5880 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
5881 cmd = ast_play_and_wait(chan, vms->fn);
5885 return cmd;
5888 /* Default English syntax */
5889 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
5891 int cmd=0;
5893 if (vms->lastmsg > -1) {
5894 cmd = play_message(chan, vmu, vms);
5895 } else {
5896 cmd = ast_play_and_wait(chan, "vm-youhave");
5897 if (!cmd)
5898 cmd = ast_play_and_wait(chan, "vm-no");
5899 if (!cmd) {
5900 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
5901 cmd = ast_play_and_wait(chan, vms->fn);
5903 if (!cmd)
5904 cmd = ast_play_and_wait(chan, "vm-messages");
5906 return cmd;
5909 /* ITALIAN syntax */
5910 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
5912 int cmd=0;
5914 if (vms->lastmsg > -1) {
5915 cmd = play_message(chan, vmu, vms);
5916 } else {
5917 cmd = ast_play_and_wait(chan, "vm-no");
5918 if (!cmd)
5919 cmd = ast_play_and_wait(chan, "vm-message");
5920 if (!cmd) {
5921 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
5922 cmd = ast_play_and_wait(chan, vms->fn);
5925 return cmd;
5928 /* SPANISH syntax */
5929 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
5931 int cmd=0;
5933 if (vms->lastmsg > -1) {
5934 cmd = play_message(chan, vmu, vms);
5935 } else {
5936 cmd = ast_play_and_wait(chan, "vm-youhaveno");
5937 if (!cmd)
5938 cmd = ast_play_and_wait(chan, "vm-messages");
5939 if (!cmd) {
5940 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
5941 cmd = ast_play_and_wait(chan, vms->fn);
5944 return cmd;
5947 /* PORTUGUESE syntax */
5948 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
5950 int cmd=0;
5952 if (vms->lastmsg > -1) {
5953 cmd = play_message(chan, vmu, vms);
5954 } else {
5955 cmd = ast_play_and_wait(chan, "vm-no");
5956 if (!cmd) {
5957 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
5958 cmd = ast_play_and_wait(chan, vms->fn);
5960 if (!cmd)
5961 cmd = ast_play_and_wait(chan, "vm-messages");
5963 return cmd;
5966 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
5968 if (!strcasecmp(chan->language, "es")) { /* SPANISH */
5969 return vm_browse_messages_es(chan, vms, vmu);
5970 } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN */
5971 return vm_browse_messages_it(chan, vms, vmu);
5972 } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE */
5973 return vm_browse_messages_pt(chan, vms, vmu);
5974 } else if (!strcasecmp(chan->language, "gr")){
5975 return vm_browse_messages_gr(chan, vms, vmu); /* GREEK */
5976 } else { /* Default to English syntax */
5977 return vm_browse_messages_en(chan, vms, vmu);
5981 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
5982 struct ast_vm_user *res_vmu, const char *context, const char *prefix,
5983 int skipuser, int maxlogins, int silent)
5985 int useadsi=0, valid=0, logretries=0;
5986 char password[AST_MAX_EXTENSION]="", *passptr;
5987 struct ast_vm_user vmus, *vmu = NULL;
5989 /* If ADSI is supported, setup login screen */
5990 adsi_begin(chan, &useadsi);
5991 if (!skipuser && useadsi)
5992 adsi_login(chan);
5993 if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
5994 ast_log(LOG_WARNING, "Couldn't stream login file\n");
5995 return -1;
5998 /* Authenticate them and get their mailbox/password */
6000 while (!valid && (logretries < maxlogins)) {
6001 /* Prompt for, and read in the username */
6002 if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
6003 ast_log(LOG_WARNING, "Couldn't read username\n");
6004 return -1;
6006 if (ast_strlen_zero(mailbox)) {
6007 if (chan->cid.cid_num) {
6008 ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
6009 } else {
6010 if (option_verbose > 2)
6011 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
6012 return -1;
6015 if (useadsi)
6016 adsi_password(chan);
6018 if (!ast_strlen_zero(prefix)) {
6019 char fullusername[80] = "";
6020 ast_copy_string(fullusername, prefix, sizeof(fullusername));
6021 strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
6022 ast_copy_string(mailbox, fullusername, mailbox_size);
6025 ast_log(LOG_DEBUG, "Before find user for mailbox %s\n",mailbox);
6026 vmu = find_user(&vmus, context, mailbox);
6027 if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
6028 /* saved password is blank, so don't bother asking */
6029 password[0] = '\0';
6030 } else {
6031 if (ast_streamfile(chan, "vm-password", chan->language)) {
6032 ast_log(LOG_WARNING, "Unable to stream password file\n");
6033 return -1;
6035 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
6036 ast_log(LOG_WARNING, "Unable to read password\n");
6037 return -1;
6041 if (vmu) {
6042 passptr = vmu->password;
6043 if (passptr[0] == '-') passptr++;
6045 if (vmu && !strcmp(passptr, password))
6046 valid++;
6047 else {
6048 if (option_verbose > 2)
6049 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
6050 if (!ast_strlen_zero(prefix))
6051 mailbox[0] = '\0';
6053 logretries++;
6054 if (!valid) {
6055 if (skipuser || logretries >= maxlogins) {
6056 if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
6057 ast_log(LOG_WARNING, "Unable to stream incorrect message\n");
6058 return -1;
6060 } else {
6061 if (useadsi)
6062 adsi_login(chan);
6063 if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
6064 ast_log(LOG_WARNING, "Unable to stream incorrect mailbox message\n");
6065 return -1;
6068 if (ast_waitstream(chan, "")) /* Channel is hung up */
6069 return -1;
6072 if (!valid && (logretries >= maxlogins)) {
6073 ast_stopstream(chan);
6074 ast_play_and_wait(chan, "vm-goodbye");
6075 return -1;
6077 if (vmu && !skipuser) {
6078 memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
6080 return 0;
6083 static int vm_execmain(struct ast_channel *chan, void *data)
6085 /* XXX This is, admittedly, some pretty horrendus code. For some
6086 reason it just seemed a lot easier to do with GOTO's. I feel
6087 like I'm back in my GWBASIC days. XXX */
6088 int res=-1;
6089 int cmd=0;
6090 int valid = 0;
6091 struct ast_module_user *u;
6092 char prefixstr[80] ="";
6093 char ext_context[256]="";
6094 int box;
6095 int useadsi = 0;
6096 int skipuser = 0;
6097 struct vm_state vms;
6098 struct ast_vm_user *vmu = NULL, vmus;
6099 char *context=NULL;
6100 int silentexit = 0;
6101 struct ast_flags flags = { 0 };
6102 signed char record_gain = 0;
6103 int play_auto = 0;
6104 int play_folder = 0;
6105 #ifdef IMAP_STORAGE
6106 int deleted = 0;
6107 #endif
6108 u = ast_module_user_add(chan);
6110 /* Add the vm_state to the active list and keep it active */
6111 memset(&vms, 0, sizeof(vms));
6112 vms.lastmsg = -1;
6114 memset(&vmus, 0, sizeof(vmus));
6116 if (chan->_state != AST_STATE_UP) {
6117 ast_log(LOG_DEBUG, "Before ast_answer\n");
6118 ast_answer(chan);
6121 if (!ast_strlen_zero(data)) {
6122 char *opts[OPT_ARG_ARRAY_SIZE];
6123 char *parse;
6124 AST_DECLARE_APP_ARGS(args,
6125 AST_APP_ARG(argv0);
6126 AST_APP_ARG(argv1);
6129 parse = ast_strdupa(data);
6131 AST_STANDARD_APP_ARGS(args, parse);
6133 if (args.argc == 2) {
6134 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
6135 ast_module_user_remove(u);
6136 return -1;
6138 if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
6139 int gain;
6140 if (opts[OPT_ARG_RECORDGAIN]) {
6141 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
6142 ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
6143 ast_module_user_remove(u);
6144 return -1;
6145 } else {
6146 record_gain = (signed char) gain;
6148 } else {
6149 ast_log(LOG_WARNING, "Invalid Gain level set with option g\n");
6152 if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
6153 play_auto = 1;
6154 if (opts[OPT_ARG_PLAYFOLDER]) {
6155 if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%d", &play_folder) != 1) {
6156 ast_log(LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
6158 } else {
6159 ast_log(LOG_WARNING, "Invalid folder set with option a\n");
6161 if ( play_folder > 9 || play_folder < 0) {
6162 ast_log(LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
6163 play_folder = 0;
6166 } else {
6167 /* old style options parsing */
6168 while (*(args.argv0)) {
6169 if (*(args.argv0) == 's')
6170 ast_set_flag(&flags, OPT_SILENT);
6171 else if (*(args.argv0) == 'p')
6172 ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
6173 else
6174 break;
6175 (args.argv0)++;
6180 valid = ast_test_flag(&flags, OPT_SILENT);
6182 if ((context = strchr(args.argv0, '@')))
6183 *context++ = '\0';
6185 if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
6186 ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
6187 else
6188 ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
6190 if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
6191 skipuser++;
6192 else
6193 valid = 0;
6196 if (!valid)
6197 res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
6199 ast_log(LOG_DEBUG, "After vm_authenticate\n");
6200 if (!res) {
6201 valid = 1;
6202 if (!skipuser)
6203 vmu = &vmus;
6204 } else {
6205 res = 0;
6208 /* If ADSI is supported, setup login screen */
6209 adsi_begin(chan, &useadsi);
6211 #ifdef IMAP_STORAGE
6212 vms.interactive = 1;
6213 vms.updated = 2;
6214 vmstate_insert(&vms);
6215 init_vm_state(&vms);
6216 #endif
6217 if (!valid)
6218 goto out;
6220 if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
6221 /* TODO: Handle memory allocation failure */
6223 if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
6224 /* TODO: Handle memory allocation failure */
6227 /* Set language from config to override channel language */
6228 if (!ast_strlen_zero(vmu->language))
6229 ast_string_field_set(chan, language, vmu->language);
6230 #ifndef IMAP_STORAGE
6231 create_dirpath(vms.curdir, sizeof(vms.curdir), vmu->context, vms.username, "");
6232 #endif
6233 /* Retrieve old and new message counts */
6234 ast_log(LOG_DEBUG, "Before open_mailbox\n");
6235 res = open_mailbox(&vms, vmu, 1);
6236 if (res == ERROR_LOCK_PATH)
6237 goto out;
6238 vms.oldmessages = vms.lastmsg + 1;
6239 ast_log(LOG_DEBUG, "Number of old messages: %d\n",vms.oldmessages);
6240 /* Start in INBOX */
6241 res = open_mailbox(&vms, vmu, 0);
6242 if (res == ERROR_LOCK_PATH)
6243 goto out;
6244 vms.newmessages = vms.lastmsg + 1;
6245 ast_log(LOG_DEBUG, "Number of new messages: %d\n",vms.newmessages);
6247 /* Select proper mailbox FIRST!! */
6248 if (play_auto) {
6249 res = open_mailbox(&vms, vmu, play_folder);
6250 if (res == ERROR_LOCK_PATH)
6251 goto out;
6253 /* If there are no new messages, inform the user and hangup */
6254 if (vms.lastmsg == -1) {
6255 cmd = vm_browse_messages(chan, &vms, vmu);
6256 res = 0;
6257 goto out;
6259 } else {
6260 if (!vms.newmessages && vms.oldmessages) {
6261 /* If we only have old messages start here */
6262 res = open_mailbox(&vms, vmu, 1);
6263 if (res == ERROR_LOCK_PATH)
6264 goto out;
6268 if (useadsi)
6269 adsi_status(chan, &vms);
6270 res = 0;
6272 /* Check to see if this is a new user */
6273 if (!strcasecmp(vmu->mailbox, vmu->password) &&
6274 (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
6275 if (ast_play_and_wait(chan, "vm-newuser") == -1)
6276 ast_log(LOG_WARNING, "Couldn't stream new user file\n");
6277 cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
6278 if ((cmd == 't') || (cmd == '#')) {
6279 /* Timeout */
6280 res = 0;
6281 goto out;
6282 } else if (cmd < 0) {
6283 /* Hangup */
6284 res = -1;
6285 goto out;
6288 #ifdef IMAP_STORAGE
6289 if(option_debug > 2)
6290 ast_log(LOG_DEBUG, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
6291 if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
6292 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!!\n");
6293 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
6295 #endif
6296 if (play_auto) {
6297 cmd = '1';
6298 } else {
6299 cmd = vm_intro(chan, vmu, &vms);
6302 vms.repeats = 0;
6303 vms.starting = 1;
6304 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
6305 /* Run main menu */
6306 switch (cmd) {
6307 case '1':
6308 vms.curmsg = 0;
6309 /* Fall through */
6310 case '5':
6311 cmd = vm_browse_messages(chan, &vms, vmu);
6312 break;
6313 case '2': /* Change folders */
6314 if (useadsi)
6315 adsi_folders(chan, 0, "Change to folder...");
6316 cmd = get_folder2(chan, "vm-changeto", 0);
6317 if (cmd == '#') {
6318 cmd = 0;
6319 } else if (cmd > 0) {
6320 cmd = cmd - '0';
6321 res = close_mailbox(&vms, vmu);
6322 if (res == ERROR_LOCK_PATH)
6323 goto out;
6324 res = open_mailbox(&vms, vmu, cmd);
6325 if (res == ERROR_LOCK_PATH)
6326 goto out;
6327 cmd = 0;
6329 if (useadsi)
6330 adsi_status2(chan, &vms);
6332 if (!cmd)
6333 cmd = vm_play_folder_name(chan, vms.vmbox);
6335 vms.starting = 1;
6336 break;
6337 case '3': /* Advanced options */
6338 cmd = 0;
6339 vms.repeats = 0;
6340 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
6341 switch (cmd) {
6342 case '1': /* Reply */
6343 if (vms.lastmsg > -1 && !vms.starting) {
6344 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
6345 if (cmd == ERROR_LOCK_PATH) {
6346 res = cmd;
6347 goto out;
6349 } else
6350 cmd = ast_play_and_wait(chan, "vm-sorry");
6351 cmd = 't';
6352 break;
6353 case '2': /* Callback */
6354 if (option_verbose > 2 && !vms.starting)
6355 ast_verbose( VERBOSE_PREFIX_3 "Callback Requested\n");
6356 if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
6357 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
6358 if (cmd == 9) {
6359 silentexit = 1;
6360 goto out;
6361 } else if (cmd == ERROR_LOCK_PATH) {
6362 res = cmd;
6363 goto out;
6366 else
6367 cmd = ast_play_and_wait(chan, "vm-sorry");
6368 cmd = 't';
6369 break;
6370 case '3': /* Envelope */
6371 if (vms.lastmsg > -1 && !vms.starting) {
6372 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
6373 if (cmd == ERROR_LOCK_PATH) {
6374 res = cmd;
6375 goto out;
6377 } else
6378 cmd = ast_play_and_wait(chan, "vm-sorry");
6379 cmd = 't';
6380 break;
6381 case '4': /* Dialout */
6382 if (!ast_strlen_zero(vmu->dialout)) {
6383 cmd = dialout(chan, vmu, NULL, vmu->dialout);
6384 if (cmd == 9) {
6385 silentexit = 1;
6386 goto out;
6389 else
6390 cmd = ast_play_and_wait(chan, "vm-sorry");
6391 cmd = 't';
6392 break;
6394 case '5': /* Leave VoiceMail */
6395 if (ast_test_flag(vmu, VM_SVMAIL)) {
6396 cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain);
6397 if (cmd == ERROR_LOCK_PATH) {
6398 res = cmd;
6399 goto out;
6401 } else
6402 cmd = ast_play_and_wait(chan,"vm-sorry");
6403 cmd='t';
6404 break;
6406 case '*': /* Return to main menu */
6407 cmd = 't';
6408 break;
6410 default:
6411 cmd = 0;
6412 if (!vms.starting) {
6413 cmd = ast_play_and_wait(chan, "vm-toreply");
6415 if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
6416 cmd = ast_play_and_wait(chan, "vm-tocallback");
6418 if (!cmd && !vms.starting) {
6419 cmd = ast_play_and_wait(chan, "vm-tohearenv");
6421 if (!ast_strlen_zero(vmu->dialout) && !cmd) {
6422 cmd = ast_play_and_wait(chan, "vm-tomakecall");
6424 if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
6425 cmd=ast_play_and_wait(chan, "vm-leavemsg");
6426 if (!cmd)
6427 cmd = ast_play_and_wait(chan, "vm-starmain");
6428 if (!cmd)
6429 cmd = ast_waitfordigit(chan,6000);
6430 if (!cmd)
6431 vms.repeats++;
6432 if (vms.repeats > 3)
6433 cmd = 't';
6436 if (cmd == 't') {
6437 cmd = 0;
6438 vms.repeats = 0;
6440 break;
6441 case '4':
6442 if (vms.curmsg) {
6443 vms.curmsg--;
6444 cmd = play_message(chan, vmu, &vms);
6445 } else {
6446 cmd = ast_play_and_wait(chan, "vm-nomore");
6448 break;
6449 case '6':
6450 if (vms.curmsg < vms.lastmsg) {
6451 vms.curmsg++;
6452 cmd = play_message(chan, vmu, &vms);
6453 } else {
6454 cmd = ast_play_and_wait(chan, "vm-nomore");
6456 break;
6457 case '7':
6458 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
6459 if (useadsi)
6460 adsi_delete(chan, &vms);
6461 if (vms.deleted[vms.curmsg])
6462 cmd = ast_play_and_wait(chan, "vm-deleted");
6463 else
6464 cmd = ast_play_and_wait(chan, "vm-undeleted");
6465 if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
6466 if (vms.curmsg < vms.lastmsg) {
6467 vms.curmsg++;
6468 cmd = play_message(chan, vmu, &vms);
6469 } else {
6470 cmd = ast_play_and_wait(chan, "vm-nomore");
6473 #ifdef IMAP_STORAGE
6474 deleted = 1;
6475 #endif
6476 break;
6478 case '8':
6479 if (vms.lastmsg > -1) {
6480 cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain);
6481 if (cmd == ERROR_LOCK_PATH) {
6482 res = cmd;
6483 goto out;
6485 } else
6486 cmd = ast_play_and_wait(chan, "vm-nomore");
6487 break;
6488 case '9':
6489 if (useadsi)
6490 adsi_folders(chan, 1, "Save to folder...");
6491 cmd = get_folder2(chan, "vm-savefolder", 1);
6492 box = 0; /* Shut up compiler */
6493 if (cmd == '#') {
6494 cmd = 0;
6495 break;
6496 } else if (cmd > 0) {
6497 box = cmd = cmd - '0';
6498 cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
6499 if (cmd == ERROR_LOCK_PATH) {
6500 res = cmd;
6501 goto out;
6502 #ifdef IMAP_STORAGE
6503 } else if (cmd == 10) {
6504 goto out;
6505 #endif
6506 } else if (!cmd) {
6507 vms.deleted[vms.curmsg] = 1;
6508 } else {
6509 vms.deleted[vms.curmsg] = 0;
6510 vms.heard[vms.curmsg] = 0;
6513 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
6514 if (useadsi)
6515 adsi_message(chan, &vms);
6516 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
6517 if (!cmd) {
6518 cmd = ast_play_and_wait(chan, "vm-message");
6519 if (!cmd)
6520 cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
6521 if (!cmd)
6522 cmd = ast_play_and_wait(chan, "vm-savedto");
6523 if (!cmd)
6524 cmd = vm_play_folder_name(chan, vms.fn);
6525 } else {
6526 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
6528 if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
6529 if (vms.curmsg < vms.lastmsg) {
6530 vms.curmsg++;
6531 cmd = play_message(chan, vmu, &vms);
6532 } else {
6533 cmd = ast_play_and_wait(chan, "vm-nomore");
6536 break;
6537 case '*':
6538 if (!vms.starting) {
6539 cmd = ast_play_and_wait(chan, "vm-onefor");
6540 if (!cmd)
6541 cmd = vm_play_folder_name(chan, vms.vmbox);
6542 if (!cmd)
6543 cmd = ast_play_and_wait(chan, "vm-opts");
6544 if (!cmd)
6545 cmd = vm_instructions(chan, &vms, 1);
6546 } else
6547 cmd = 0;
6548 break;
6549 case '0':
6550 cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
6551 if (useadsi)
6552 adsi_status(chan, &vms);
6553 break;
6554 default: /* Nothing */
6555 cmd = vm_instructions(chan, &vms, 0);
6556 break;
6559 if ((cmd == 't') || (cmd == '#')) {
6560 /* Timeout */
6561 res = 0;
6562 } else {
6563 /* Hangup */
6564 res = -1;
6567 out:
6568 if (res > -1) {
6569 ast_stopstream(chan);
6570 adsi_goodbye(chan);
6571 if (valid) {
6572 if (silentexit)
6573 res = ast_play_and_wait(chan, "vm-dialout");
6574 else
6575 res = ast_play_and_wait(chan, "vm-goodbye");
6576 if (res > 0)
6577 res = 0;
6579 if (useadsi)
6580 ast_adsi_unload_session(chan);
6582 if (vmu)
6583 close_mailbox(&vms, vmu);
6584 if (valid) {
6585 snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
6586 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
6587 run_externnotify(vmu->context, vmu->mailbox);
6589 #ifdef IMAP_STORAGE
6590 /* expunge message - use UID Expunge if supported on IMAP server*/
6591 if(option_debug > 2)
6592 ast_log(LOG_DEBUG, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
6593 if (vmu && deleted == 1 && expungeonhangup == 1) {
6594 #ifdef HAVE_IMAP_TK2006
6595 if (LEVELUIDPLUS (vms.mailstream)) {
6596 mail_expunge_full(vms.mailstream,NIL,EX_UID);
6597 } else
6598 #endif
6599 mail_expunge(vms.mailstream);
6601 /* before we delete the state, we should copy pertinent info
6602 * back to the persistent model */
6603 vmstate_delete(&vms);
6604 #endif
6605 if (vmu)
6606 free_user(vmu);
6607 if (vms.deleted)
6608 free(vms.deleted);
6609 if (vms.heard)
6610 free(vms.heard);
6611 ast_module_user_remove(u);
6613 return res;
6616 static int vm_exec(struct ast_channel *chan, void *data)
6618 int res = 0;
6619 struct ast_module_user *u;
6620 char *tmp;
6621 struct leave_vm_options leave_options;
6622 struct ast_flags flags = { 0 };
6623 static int deprecate_warning = 0;
6624 char *opts[OPT_ARG_ARRAY_SIZE];
6625 AST_DECLARE_APP_ARGS(args,
6626 AST_APP_ARG(argv0);
6627 AST_APP_ARG(argv1);
6630 u = ast_module_user_add(chan);
6632 memset(&leave_options, 0, sizeof(leave_options));
6634 if (chan->_state != AST_STATE_UP)
6635 ast_answer(chan);
6637 if (!ast_strlen_zero(data)) {
6638 tmp = ast_strdupa(data);
6639 AST_STANDARD_APP_ARGS(args, tmp);
6640 if (args.argc == 2) {
6641 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
6642 ast_module_user_remove(u);
6643 return -1;
6645 ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_PRIORITY_JUMP);
6646 if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
6647 int gain;
6649 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
6650 ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
6651 ast_module_user_remove(u);
6652 return -1;
6653 } else {
6654 leave_options.record_gain = (signed char) gain;
6657 } else {
6658 /* old style options parsing */
6659 int old = 0;
6660 char *orig_argv0 = args.argv0;
6661 while (*(args.argv0)) {
6662 if (*(args.argv0) == 's') {
6663 old = 1;
6664 ast_set_flag(&leave_options, OPT_SILENT);
6665 } else if (*(args.argv0) == 'b') {
6666 old = 1;
6667 ast_set_flag(&leave_options, OPT_BUSY_GREETING);
6668 } else if (*(args.argv0) == 'u') {
6669 old = 1;
6670 ast_set_flag(&leave_options, OPT_UNAVAIL_GREETING);
6671 } else if (*(args.argv0) == 'j') {
6672 old = 1;
6673 ast_set_flag(&leave_options, OPT_PRIORITY_JUMP);
6674 } else
6675 break;
6676 (args.argv0)++;
6678 if (!deprecate_warning && old) {
6679 deprecate_warning = 1;
6680 ast_log(LOG_WARNING, "Prefixing the mailbox with an option is deprecated ('%s').\n", orig_argv0);
6681 ast_log(LOG_WARNING, "Please move all leading options to the second argument.\n");
6684 } else {
6685 char tmp[256];
6686 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
6687 if (res < 0) {
6688 ast_module_user_remove(u);
6689 return res;
6691 if (ast_strlen_zero(tmp)) {
6692 ast_module_user_remove(u);
6693 return 0;
6695 args.argv0 = ast_strdupa(tmp);
6698 res = leave_voicemail(chan, args.argv0, &leave_options);
6700 if (res == ERROR_LOCK_PATH) {
6701 ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
6702 /*Send the call to n+101 priority, where n is the current priority*/
6703 if (ast_test_flag(&leave_options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
6704 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
6705 ast_log(LOG_WARNING, "Extension %s, priority %d doesn't exist.\n", chan->exten, chan->priority + 101);
6706 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
6707 res = 0;
6710 ast_module_user_remove(u);
6712 return res;
6715 static struct ast_vm_user *find_or_create(char *context, char *mbox)
6717 struct ast_vm_user *vmu;
6718 AST_LIST_TRAVERSE(&users, vmu, list) {
6719 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mbox, vmu->mailbox))
6720 break;
6721 if (context && (!strcasecmp(context, vmu->context)) && (!strcasecmp(mbox, vmu->mailbox)))
6722 break;
6725 if (!vmu) {
6726 if ((vmu = ast_calloc(1, sizeof(*vmu)))) {
6727 ast_copy_string(vmu->context, context, sizeof(vmu->context));
6728 ast_copy_string(vmu->mailbox, mbox, sizeof(vmu->mailbox));
6729 AST_LIST_INSERT_TAIL(&users, vmu, list);
6732 return vmu;
6735 static int append_mailbox(char *context, char *mbox, char *data)
6737 /* Assumes lock is already held */
6738 char *tmp;
6739 char *stringp;
6740 char *s;
6741 struct ast_vm_user *vmu;
6743 tmp = ast_strdupa(data);
6745 if ((vmu = find_or_create(context, mbox))) {
6746 populate_defaults(vmu);
6748 stringp = tmp;
6749 if ((s = strsep(&stringp, ",")))
6750 ast_copy_string(vmu->password, s, sizeof(vmu->password));
6751 if (stringp && (s = strsep(&stringp, ",")))
6752 ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
6753 if (stringp && (s = strsep(&stringp, ",")))
6754 ast_copy_string(vmu->email, s, sizeof(vmu->email));
6755 if (stringp && (s = strsep(&stringp, ",")))
6756 ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
6757 if (stringp && (s = strsep(&stringp, ",")))
6758 apply_options(vmu, s);
6760 return 0;
6763 static int vm_box_exists(struct ast_channel *chan, void *data)
6765 struct ast_module_user *u;
6766 struct ast_vm_user svm;
6767 char *context, *box;
6768 int priority_jump = 0;
6769 AST_DECLARE_APP_ARGS(args,
6770 AST_APP_ARG(mbox);
6771 AST_APP_ARG(options);
6774 if (ast_strlen_zero(data)) {
6775 ast_log(LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
6776 return -1;
6779 u = ast_module_user_add(chan);
6781 box = ast_strdupa(data);
6783 AST_STANDARD_APP_ARGS(args, box);
6785 if (args.options) {
6786 if (strchr(args.options, 'j'))
6787 priority_jump = 1;
6790 if ((context = strchr(args.mbox, '@'))) {
6791 *context = '\0';
6792 context++;
6795 if (find_user(&svm, context, args.mbox)) {
6796 pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
6797 if (priority_jump || ast_opt_priority_jumping)
6798 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
6799 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);
6800 } else
6801 pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
6802 ast_module_user_remove(u);
6803 return 0;
6806 static int vmauthenticate(struct ast_channel *chan, void *data)
6808 struct ast_module_user *u;
6809 char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
6810 struct ast_vm_user vmus;
6811 char *options = NULL;
6812 int silent = 0, skipuser = 0;
6813 int res = -1;
6815 u = ast_module_user_add(chan);
6817 if (s) {
6818 s = ast_strdupa(s);
6819 user = strsep(&s, "|");
6820 options = strsep(&s, "|");
6821 if (user) {
6822 s = user;
6823 user = strsep(&s, "@");
6824 context = strsep(&s, "");
6825 if (!ast_strlen_zero(user))
6826 skipuser++;
6827 ast_copy_string(mailbox, user, sizeof(mailbox));
6831 if (options) {
6832 silent = (strchr(options, 's')) != NULL;
6835 if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
6836 pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
6837 pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
6838 ast_play_and_wait(chan, "auth-thankyou");
6839 res = 0;
6842 ast_module_user_remove(u);
6843 return res;
6846 static char voicemail_show_users_help[] =
6847 "Usage: voicemail show users [for <context>]\n"
6848 " Lists all mailboxes currently set up\n";
6850 static char voicemail_show_zones_help[] =
6851 "Usage: voicemail show zones\n"
6852 " Lists zone message formats\n";
6854 static int handle_voicemail_show_users(int fd, int argc, char *argv[])
6856 struct ast_vm_user *vmu;
6857 char *output_format = "%-10s %-5s %-25s %-10s %6s\n";
6859 if ((argc < 3) || (argc > 5) || (argc == 4)) return RESULT_SHOWUSAGE;
6860 else if ((argc == 5) && strcmp(argv[3],"for")) return RESULT_SHOWUSAGE;
6862 AST_LIST_LOCK(&users);
6863 if (!AST_LIST_EMPTY(&users)) {
6864 if (argc == 3)
6865 ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
6866 else {
6867 int count = 0;
6868 AST_LIST_TRAVERSE(&users, vmu, list) {
6869 if (!strcmp(argv[4],vmu->context))
6870 count++;
6872 if (count) {
6873 ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
6874 } else {
6875 ast_cli(fd, "No such voicemail context \"%s\"\n", argv[4]);
6876 AST_LIST_UNLOCK(&users);
6877 return RESULT_FAILURE;
6880 AST_LIST_TRAVERSE(&users, vmu, list) {
6881 int newmsgs = 0, oldmsgs = 0;
6882 char count[12], tmp[256] = "";
6884 if ((argc == 3) || ((argc == 5) && !strcmp(argv[4],vmu->context))) {
6885 snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
6886 inboxcount(tmp, &newmsgs, &oldmsgs);
6887 snprintf(count,sizeof(count),"%d",newmsgs);
6888 ast_cli(fd, output_format, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
6891 } else {
6892 ast_cli(fd, "There are no voicemail users currently defined\n");
6893 AST_LIST_UNLOCK(&users);
6894 return RESULT_FAILURE;
6896 AST_LIST_UNLOCK(&users);
6897 return RESULT_SUCCESS;
6900 static int handle_voicemail_show_zones(int fd, int argc, char *argv[])
6902 struct vm_zone *zone;
6903 char *output_format = "%-15s %-20s %-45s\n";
6904 int res = RESULT_SUCCESS;
6906 if (argc != 3)
6907 return RESULT_SHOWUSAGE;
6909 AST_LIST_LOCK(&zones);
6910 if (!AST_LIST_EMPTY(&zones)) {
6911 ast_cli(fd, output_format, "Zone", "Timezone", "Message Format");
6912 AST_LIST_TRAVERSE(&zones, zone, list) {
6913 ast_cli(fd, output_format, zone->name, zone->timezone, zone->msg_format);
6915 } else {
6916 ast_cli(fd, "There are no voicemail zones currently defined\n");
6917 res = RESULT_FAILURE;
6919 AST_LIST_UNLOCK(&zones);
6921 return res;
6924 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
6926 int which = 0;
6927 int wordlen;
6928 struct ast_vm_user *vmu;
6929 const char *context = "";
6931 /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
6932 if (pos > 4)
6933 return NULL;
6934 if (pos == 3)
6935 return (state == 0) ? ast_strdup("for") : NULL;
6936 wordlen = strlen(word);
6937 AST_LIST_TRAVERSE(&users, vmu, list) {
6938 if (!strncasecmp(word, vmu->context, wordlen)) {
6939 if (context && strcmp(context, vmu->context) && ++which > state)
6940 return ast_strdup(vmu->context);
6941 /* ignore repeated contexts ? */
6942 context = vmu->context;
6945 return NULL;
6948 static struct ast_cli_entry cli_show_voicemail_users_deprecated = {
6949 { "show", "voicemail", "users", NULL },
6950 handle_voicemail_show_users, NULL,
6951 NULL, complete_voicemail_show_users };
6953 static struct ast_cli_entry cli_show_voicemail_zones_deprecated = {
6954 { "show", "voicemail", "zones", NULL },
6955 handle_voicemail_show_zones, NULL,
6956 NULL, NULL };
6958 static struct ast_cli_entry cli_voicemail[] = {
6959 { { "voicemail", "show", "users", NULL },
6960 handle_voicemail_show_users, "List defined voicemail boxes",
6961 voicemail_show_users_help, complete_voicemail_show_users, &cli_show_voicemail_users_deprecated },
6963 { { "voicemail", "show", "zones", NULL },
6964 handle_voicemail_show_zones, "List zone message formats",
6965 voicemail_show_zones_help, NULL, &cli_show_voicemail_zones_deprecated },
6968 static int load_config(void)
6970 struct ast_vm_user *cur;
6971 struct vm_zone *zcur;
6972 struct ast_config *cfg, *ucfg;
6973 char *cat;
6974 struct ast_variable *var;
6975 const char *notifystr = NULL;
6976 const char *smdistr = NULL;
6977 const char *astattach;
6978 const char *astsearch;
6979 const char *astsaycid;
6980 const char *send_voicemail;
6981 #ifdef IMAP_STORAGE
6982 const char *imap_server;
6983 const char *imap_port;
6984 const char *imap_flags;
6985 const char *imap_folder;
6986 const char *auth_user;
6987 const char *auth_password;
6988 const char *expunge_on_hangup;
6989 #endif
6990 const char *astcallop;
6991 const char *astreview;
6992 const char *asttempgreetwarn;
6993 const char *astskipcmd;
6994 const char *asthearenv;
6995 const char *astsaydurationinfo;
6996 const char *astsaydurationminfo;
6997 const char *silencestr;
6998 const char *maxmsgstr;
6999 const char *astdirfwd;
7000 const char *thresholdstr;
7001 const char *fmt;
7002 const char *astemail;
7003 const char *ucontext;
7004 const char *astmailcmd = SENDMAIL;
7005 const char *astforcename;
7006 const char *astforcegreet;
7007 const char *s;
7008 char *q,*stringp;
7009 const char *dialoutcxt = NULL;
7010 const char *callbackcxt = NULL;
7011 const char *exitcxt = NULL;
7012 const char *extpc;
7013 const char *emaildateformatstr;
7014 const char *volgainstr;
7015 int x;
7016 int tmpadsi[4];
7018 cfg = ast_config_load(VOICEMAIL_CONFIG);
7020 AST_LIST_LOCK(&users);
7021 while ((cur = AST_LIST_REMOVE_HEAD(&users, list))) {
7022 ast_set_flag(cur, VM_ALLOCED);
7023 free_user(cur);
7026 AST_LIST_LOCK(&zones);
7027 while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
7028 free_zone(zcur);
7029 AST_LIST_UNLOCK(&zones);
7031 memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
7033 if (cfg) {
7034 /* General settings */
7036 if (!(ucontext = ast_variable_retrieve(cfg, "general", "userscontext")))
7037 ucontext = "default";
7038 ast_copy_string(userscontext, ucontext, sizeof(userscontext));
7039 /* Attach voice message to mail message ? */
7040 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach")))
7041 astattach = "yes";
7042 ast_set2_flag((&globalflags), ast_true(astattach), VM_ATTACH);
7044 if (!(astsearch = ast_variable_retrieve(cfg, "general", "searchcontexts")))
7045 astsearch = "no";
7046 ast_set2_flag((&globalflags), ast_true(astsearch), VM_SEARCH);
7048 volgain = 0.0;
7049 if ((volgainstr = ast_variable_retrieve(cfg, "general", "volgain")))
7050 sscanf(volgainstr, "%lf", &volgain);
7052 #ifdef ODBC_STORAGE
7053 strcpy(odbc_database, "asterisk");
7054 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
7055 ast_copy_string(odbc_database, thresholdstr, sizeof(odbc_database));
7057 strcpy(odbc_table, "voicemessages");
7058 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbctable"))) {
7059 ast_copy_string(odbc_table, thresholdstr, sizeof(odbc_table));
7061 #endif
7062 /* Mail command */
7063 strcpy(mailcmd, SENDMAIL);
7064 if ((astmailcmd = ast_variable_retrieve(cfg, "general", "mailcmd")))
7065 ast_copy_string(mailcmd, astmailcmd, sizeof(mailcmd)); /* User setting */
7067 maxsilence = 0;
7068 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
7069 maxsilence = atoi(silencestr);
7070 if (maxsilence > 0)
7071 maxsilence *= 1000;
7074 if (!(maxmsgstr = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
7075 maxmsg = MAXMSG;
7076 } else {
7077 maxmsg = atoi(maxmsgstr);
7078 if (maxmsg <= 0) {
7079 ast_log(LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", maxmsgstr, MAXMSG);
7080 maxmsg = MAXMSG;
7081 } else if (maxmsg > MAXMSGLIMIT) {
7082 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, maxmsgstr);
7083 maxmsg = MAXMSGLIMIT;
7087 /* Load date format config for voicemail mail */
7088 if ((emaildateformatstr = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
7089 ast_copy_string(emaildateformat, emaildateformatstr, sizeof(emaildateformat));
7092 /* External password changing command */
7093 if ((extpc = ast_variable_retrieve(cfg, "general", "externpass"))) {
7094 ast_copy_string(ext_pass_cmd,extpc,sizeof(ext_pass_cmd));
7096 #ifdef IMAP_STORAGE
7097 /* IMAP server address */
7098 if ((imap_server = ast_variable_retrieve(cfg, "general", "imapserver"))) {
7099 ast_copy_string(imapserver, imap_server, sizeof(imapserver));
7100 } else {
7101 ast_copy_string(imapserver,"localhost", sizeof(imapserver));
7103 /* IMAP server port */
7104 if ((imap_port = ast_variable_retrieve(cfg, "general", "imapport"))) {
7105 ast_copy_string(imapport, imap_port, sizeof(imapport));
7106 } else {
7107 ast_copy_string(imapport,"143", sizeof(imapport));
7109 /* IMAP server flags */
7110 if ((imap_flags = ast_variable_retrieve(cfg, "general", "imapflags"))) {
7111 ast_copy_string(imapflags, imap_flags, sizeof(imapflags));
7113 /* IMAP server master username */
7114 if ((auth_user = ast_variable_retrieve(cfg, "general", "authuser"))) {
7115 ast_copy_string(authuser, auth_user, sizeof(authuser));
7117 /* IMAP server master password */
7118 if ((auth_password = ast_variable_retrieve(cfg, "general", "authpassword"))) {
7119 ast_copy_string(authpassword, auth_password, sizeof(authpassword));
7121 /* Expunge on exit */
7122 if ((expunge_on_hangup = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
7123 if(ast_false(expunge_on_hangup))
7124 expungeonhangup = 0;
7125 else
7126 expungeonhangup = 1;
7127 } else {
7128 expungeonhangup = 1;
7130 /* IMAP voicemail folder */
7131 if ((imap_folder = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
7132 ast_copy_string(imapfolder, imap_folder, sizeof(imapfolder));
7133 } else {
7134 ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
7136 #endif
7137 /* External voicemail notify application */
7139 if ((notifystr = ast_variable_retrieve(cfg, "general", "externnotify"))) {
7140 ast_copy_string(externnotify, notifystr, sizeof(externnotify));
7141 ast_log(LOG_DEBUG, "found externnotify: %s\n", externnotify);
7142 if (!strcasecmp(externnotify, "smdi")) {
7143 ast_log(LOG_DEBUG, "Using SMDI for external voicemail notification\n");
7144 if ((smdistr = ast_variable_retrieve(cfg, "general", "smdiport"))) {
7145 smdi_iface = ast_smdi_interface_find(smdistr);
7146 } else {
7147 ast_log(LOG_DEBUG, "No SMDI interface set, trying default (/dev/ttyS0)\n");
7148 smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
7151 if (!smdi_iface) {
7152 ast_log(LOG_ERROR, "No valid SMDI interface specfied, disabling external voicemail notification\n");
7153 externnotify[0] = '\0';
7154 } else {
7155 ast_log(LOG_DEBUG, "Using SMDI port %s\n", smdi_iface->name);
7158 } else {
7159 externnotify[0] = '\0';
7162 /* Silence treshold */
7163 silencethreshold = 256;
7164 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
7165 silencethreshold = atoi(thresholdstr);
7167 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
7168 astemail = ASTERISK_USERNAME;
7169 ast_copy_string(serveremail, astemail, sizeof(serveremail));
7171 vmmaxmessage = 0;
7172 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
7173 if (sscanf(s, "%d", &x) == 1) {
7174 vmmaxmessage = x;
7175 } else {
7176 ast_log(LOG_WARNING, "Invalid max message time length\n");
7180 vmminmessage = 0;
7181 if ((s = ast_variable_retrieve(cfg, "general", "minmessage"))) {
7182 if (sscanf(s, "%d", &x) == 1) {
7183 vmminmessage = x;
7184 if (maxsilence <= vmminmessage)
7185 ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
7186 } else {
7187 ast_log(LOG_WARNING, "Invalid min message time length\n");
7190 fmt = ast_variable_retrieve(cfg, "general", "format");
7191 if (!fmt)
7192 fmt = "wav";
7193 ast_copy_string(vmfmts, fmt, sizeof(vmfmts));
7195 skipms = 3000;
7196 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
7197 if (sscanf(s, "%d", &x) == 1) {
7198 maxgreet = x;
7199 } else {
7200 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
7204 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
7205 if (sscanf(s, "%d", &x) == 1) {
7206 skipms = x;
7207 } else {
7208 ast_log(LOG_WARNING, "Invalid skipms value\n");
7212 maxlogins = 3;
7213 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
7214 if (sscanf(s, "%d", &x) == 1) {
7215 maxlogins = x;
7216 } else {
7217 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
7221 /* Force new user to record name ? */
7222 if (!(astforcename = ast_variable_retrieve(cfg, "general", "forcename")))
7223 astforcename = "no";
7224 ast_set2_flag((&globalflags), ast_true(astforcename), VM_FORCENAME);
7226 /* Force new user to record greetings ? */
7227 if (!(astforcegreet = ast_variable_retrieve(cfg, "general", "forcegreetings")))
7228 astforcegreet = "no";
7229 ast_set2_flag((&globalflags), ast_true(astforcegreet), VM_FORCEGREET);
7231 if ((s = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))){
7232 ast_log(LOG_DEBUG,"VM_CID Internal context string: %s\n",s);
7233 stringp = ast_strdupa(s);
7234 for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
7235 if (!ast_strlen_zero(stringp)) {
7236 q = strsep(&stringp,",");
7237 while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
7238 q++;
7239 ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
7240 ast_log(LOG_DEBUG,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
7241 } else {
7242 cidinternalcontexts[x][0] = '\0';
7246 if (!(astreview = ast_variable_retrieve(cfg, "general", "review"))){
7247 ast_log(LOG_DEBUG,"VM Review Option disabled globally\n");
7248 astreview = "no";
7250 ast_set2_flag((&globalflags), ast_true(astreview), VM_REVIEW);
7252 /*Temperary greeting reminder */
7253 if (!(asttempgreetwarn = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
7254 ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option disabled globally\n");
7255 asttempgreetwarn = "no";
7256 } else {
7257 ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option enabled globally\n");
7259 ast_set2_flag((&globalflags), ast_true(asttempgreetwarn), VM_TEMPGREETWARN);
7261 if (!(astcallop = ast_variable_retrieve(cfg, "general", "operator"))){
7262 ast_log(LOG_DEBUG,"VM Operator break disabled globally\n");
7263 astcallop = "no";
7265 ast_set2_flag((&globalflags), ast_true(astcallop), VM_OPERATOR);
7267 if (!(astsaycid = ast_variable_retrieve(cfg, "general", "saycid"))) {
7268 ast_log(LOG_DEBUG,"VM CID Info before msg disabled globally\n");
7269 astsaycid = "no";
7271 ast_set2_flag((&globalflags), ast_true(astsaycid), VM_SAYCID);
7273 if (!(send_voicemail = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
7274 ast_log(LOG_DEBUG,"Send Voicemail msg disabled globally\n");
7275 send_voicemail = "no";
7277 ast_set2_flag((&globalflags), ast_true(send_voicemail), VM_SVMAIL);
7279 if (!(asthearenv = ast_variable_retrieve(cfg, "general", "envelope"))) {
7280 ast_log(LOG_DEBUG,"ENVELOPE before msg enabled globally\n");
7281 asthearenv = "yes";
7283 ast_set2_flag((&globalflags), ast_true(asthearenv), VM_ENVELOPE);
7285 if (!(astsaydurationinfo = ast_variable_retrieve(cfg, "general", "sayduration"))) {
7286 ast_log(LOG_DEBUG,"Duration info before msg enabled globally\n");
7287 astsaydurationinfo = "yes";
7289 ast_set2_flag((&globalflags), ast_true(astsaydurationinfo), VM_SAYDURATION);
7291 saydurationminfo = 2;
7292 if ((astsaydurationminfo = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
7293 if (sscanf(astsaydurationminfo, "%d", &x) == 1) {
7294 saydurationminfo = x;
7295 } else {
7296 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
7300 if (!(astskipcmd = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
7301 ast_log(LOG_DEBUG,"We are not going to skip to the next msg after save/delete\n");
7302 astskipcmd = "no";
7304 ast_set2_flag((&globalflags), ast_true(astskipcmd), VM_SKIPAFTERCMD);
7306 if ((dialoutcxt = ast_variable_retrieve(cfg, "general", "dialout"))) {
7307 ast_copy_string(dialcontext, dialoutcxt, sizeof(dialcontext));
7308 ast_log(LOG_DEBUG, "found dialout context: %s\n", dialcontext);
7309 } else {
7310 dialcontext[0] = '\0';
7313 if ((callbackcxt = ast_variable_retrieve(cfg, "general", "callback"))) {
7314 ast_copy_string(callcontext, callbackcxt, sizeof(callcontext));
7315 ast_log(LOG_DEBUG, "found callback context: %s\n", callcontext);
7316 } else {
7317 callcontext[0] = '\0';
7320 if ((exitcxt = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
7321 ast_copy_string(exitcontext, exitcxt, sizeof(exitcontext));
7322 ast_log(LOG_DEBUG, "found operator context: %s\n", exitcontext);
7323 } else {
7324 exitcontext[0] = '\0';
7327 if (!(astdirfwd = ast_variable_retrieve(cfg, "general", "usedirectory")))
7328 astdirfwd = "no";
7329 ast_set2_flag((&globalflags), ast_true(astdirfwd), VM_DIRECFORWARD);
7330 if ((ucfg = ast_config_load("users.conf"))) {
7331 for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
7332 if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
7333 continue;
7334 if ((cur = find_or_create(userscontext, cat))) {
7335 populate_defaults(cur);
7336 apply_options_full(cur, ast_variable_browse(ucfg, cat));
7337 ast_copy_string(cur->context, userscontext, sizeof(cur->context));
7340 ast_config_destroy(ucfg);
7342 cat = ast_category_browse(cfg, NULL);
7343 while (cat) {
7344 if (strcasecmp(cat, "general")) {
7345 var = ast_variable_browse(cfg, cat);
7346 if (strcasecmp(cat, "zonemessages")) {
7347 /* Process mailboxes in this context */
7348 while (var) {
7349 append_mailbox(cat, var->name, var->value);
7350 var = var->next;
7352 } else {
7353 /* Timezones in this context */
7354 while (var) {
7355 struct vm_zone *z;
7356 if ((z = ast_malloc(sizeof(*z)))) {
7357 char *msg_format, *timezone;
7358 msg_format = ast_strdupa(var->value);
7359 timezone = strsep(&msg_format, "|");
7360 if (msg_format) {
7361 ast_copy_string(z->name, var->name, sizeof(z->name));
7362 ast_copy_string(z->timezone, timezone, sizeof(z->timezone));
7363 ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
7364 AST_LIST_LOCK(&zones);
7365 AST_LIST_INSERT_HEAD(&zones, z, list);
7366 AST_LIST_UNLOCK(&zones);
7367 } else {
7368 ast_log(LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
7369 free(z);
7371 } else {
7372 free(z);
7373 AST_LIST_UNLOCK(&users);
7374 ast_config_destroy(cfg);
7375 return -1;
7377 var = var->next;
7381 cat = ast_category_browse(cfg, cat);
7383 memset(fromstring,0,sizeof(fromstring));
7384 memset(pagerfromstring,0,sizeof(pagerfromstring));
7385 memset(emailtitle,0,sizeof(emailtitle));
7386 strcpy(charset, "ISO-8859-1");
7387 if (emailbody) {
7388 free(emailbody);
7389 emailbody = NULL;
7391 if (emailsubject) {
7392 free(emailsubject);
7393 emailsubject = NULL;
7395 if (pagerbody) {
7396 free(pagerbody);
7397 pagerbody = NULL;
7399 if (pagersubject) {
7400 free(pagersubject);
7401 pagersubject = NULL;
7403 if ((s = ast_variable_retrieve(cfg, "general", "pbxskip")))
7404 ast_set2_flag((&globalflags), ast_true(s), VM_PBXSKIP);
7405 if ((s = ast_variable_retrieve(cfg, "general", "fromstring")))
7406 ast_copy_string(fromstring,s,sizeof(fromstring));
7407 if ((s = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
7408 ast_copy_string(pagerfromstring,s,sizeof(pagerfromstring));
7409 if ((s = ast_variable_retrieve(cfg, "general", "charset")))
7410 ast_copy_string(charset,s,sizeof(charset));
7411 if ((s = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
7412 sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
7413 for (x = 0; x < 4; x++) {
7414 memcpy(&adsifdn[x], &tmpadsi[x], 1);
7417 if ((s = ast_variable_retrieve(cfg, "general", "adsisec"))) {
7418 sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
7419 for (x = 0; x < 4; x++) {
7420 memcpy(&adsisec[x], &tmpadsi[x], 1);
7423 if ((s = ast_variable_retrieve(cfg, "general", "adsiver")))
7424 if (atoi(s)) {
7425 adsiver = atoi(s);
7427 if ((s = ast_variable_retrieve(cfg, "general", "emailtitle"))) {
7428 ast_log(LOG_NOTICE, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
7429 ast_copy_string(emailtitle,s,sizeof(emailtitle));
7431 if ((s = ast_variable_retrieve(cfg, "general", "emailsubject")))
7432 emailsubject = ast_strdup(s);
7433 if ((s = ast_variable_retrieve(cfg, "general", "emailbody"))) {
7434 char *tmpread, *tmpwrite;
7435 emailbody = ast_strdup(s);
7437 /* substitute strings \t and \n into the appropriate characters */
7438 tmpread = tmpwrite = emailbody;
7439 while ((tmpwrite = strchr(tmpread,'\\'))) {
7440 switch (tmpwrite[1]) {
7441 case 'r':
7442 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7443 *tmpwrite = '\r';
7444 break;
7445 case 'n':
7446 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7447 *tmpwrite = '\n';
7448 break;
7449 case 't':
7450 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7451 *tmpwrite = '\t';
7452 break;
7453 default:
7454 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
7456 tmpread = tmpwrite + 1;
7459 if ((s = ast_variable_retrieve(cfg, "general", "pagersubject")))
7460 pagersubject = ast_strdup(s);
7461 if ((s = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
7462 char *tmpread, *tmpwrite;
7463 pagerbody = ast_strdup(s);
7465 /* substitute strings \t and \n into the appropriate characters */
7466 tmpread = tmpwrite = pagerbody;
7467 while ((tmpwrite = strchr(tmpread, '\\'))) {
7468 switch (tmpwrite[1]) {
7469 case 'r':
7470 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7471 *tmpwrite = '\r';
7472 break;
7473 case 'n':
7474 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7475 *tmpwrite = '\n';
7476 break;
7477 case 't':
7478 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
7479 *tmpwrite = '\t';
7480 break;
7481 default:
7482 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
7484 tmpread = tmpwrite + 1;
7487 AST_LIST_UNLOCK(&users);
7488 ast_config_destroy(cfg);
7489 return 0;
7490 } else {
7491 AST_LIST_UNLOCK(&users);
7492 ast_log(LOG_WARNING, "Failed to load configuration file. Module not activated.\n");
7493 return 0;
7497 static int reload(void)
7499 return(load_config());
7502 static int unload_module(void)
7504 int res;
7506 res = ast_unregister_application(app);
7507 res |= ast_unregister_application(app2);
7508 res |= ast_unregister_application(app3);
7509 res |= ast_unregister_application(app4);
7510 ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
7511 ast_uninstall_vm_functions();
7513 ast_module_user_hangup_all();
7515 return res;
7518 static int load_module(void)
7520 int res;
7521 res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
7522 res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
7523 res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
7524 res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
7525 if (res)
7526 return(res);
7528 if ((res=load_config())) {
7529 return(res);
7532 ast_cli_register_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
7534 /* compute the location of the voicemail spool directory */
7535 snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
7537 ast_install_vm_functions(has_voicemail, inboxcount, messagecount);
7539 return res;
7542 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
7544 int cmd = 0;
7545 char destination[80] = "";
7546 int retries = 0;
7548 if (!num) {
7549 if (option_verbose > 2)
7550 ast_verbose( VERBOSE_PREFIX_3 "Destination number will be entered manually\n");
7551 while (retries < 3 && cmd != 't') {
7552 destination[1] = '\0';
7553 destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
7554 if (!cmd)
7555 destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
7556 if (!cmd)
7557 destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
7558 if (!cmd) {
7559 cmd = ast_waitfordigit(chan, 6000);
7560 if (cmd)
7561 destination[0] = cmd;
7563 if (!cmd) {
7564 retries++;
7565 } else {
7567 if (cmd < 0)
7568 return 0;
7569 if (cmd == '*') {
7570 if (option_verbose > 2)
7571 ast_verbose( VERBOSE_PREFIX_3 "User hit '*' to cancel outgoing call\n");
7572 return 0;
7574 if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0)
7575 retries++;
7576 else
7577 cmd = 't';
7580 if (retries >= 3) {
7581 return 0;
7584 } else {
7585 if (option_verbose > 2)
7586 ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
7587 ast_copy_string(destination, num, sizeof(destination));
7590 if (!ast_strlen_zero(destination)) {
7591 if (destination[strlen(destination) -1 ] == '*')
7592 return 0;
7593 if (option_verbose > 2)
7594 ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
7595 ast_copy_string(chan->exten, destination, sizeof(chan->exten));
7596 ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
7597 chan->priority = 0;
7598 return 9;
7600 return 0;
7603 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)
7605 int res = 0;
7606 #ifdef IMAP_STORAGE
7607 char origtimeS[256],cidS[256],contextS[256];
7608 char *header_content,*temp;
7609 #endif
7610 char filename[256];
7611 struct ast_config *msg_cfg = NULL;
7612 const char *origtime, *context;
7613 char *cid, *name, *num;
7614 int retries = 0;
7616 vms->starting = 0;
7617 #ifdef IMAP_STORAGE
7618 /* START HERE */
7619 /* get the message info!! */
7620 if(option_debug > 2)
7621 ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
7622 if (vms->msgArray[vms->curmsg] == 0) {
7623 ast_log (LOG_WARNING,"Trying to access unknown message\n");
7624 return -1;
7627 /* This will only work for new messages... */
7628 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
7629 /* empty string means no valid header */
7630 if (ast_strlen_zero(header_content)) {
7631 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
7632 return -1;
7635 /* Get info from headers!! */
7636 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:");
7638 if (temp)
7639 ast_copy_string(cidS,temp, sizeof(cidS));
7640 else
7641 cidS[0] = '\0';
7643 cid = &cidS[0];
7644 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Context:");
7646 if (temp)
7647 ast_copy_string(contextS,temp, sizeof(contextS));
7648 else
7649 contextS[0] = '\0';
7651 context = &contextS[0];
7652 temp = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:");
7654 if (temp)
7655 ast_copy_string(origtimeS,temp, sizeof(origtimeS));
7656 else
7657 origtimeS[0] = '\0';
7659 origtime = &origtimeS[0];
7661 ast_copy_string(filename, "IMAP_STORAGE", sizeof(filename));
7662 #else
7663 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
7665 /* Retrieve info from VM attribute file */
7667 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
7668 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
7669 RETRIEVE(vms->curdir, vms->curmsg);
7670 msg_cfg = ast_config_load(filename);
7671 DISPOSE(vms->curdir, vms->curmsg);
7672 if (!msg_cfg) {
7673 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
7674 return 0;
7677 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
7678 ast_config_destroy(msg_cfg);
7679 return 0;
7682 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
7684 context = ast_variable_retrieve(msg_cfg, "message", "context");
7685 if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
7686 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
7687 #endif
7688 switch (option) {
7689 case 3:
7690 if (!res)
7691 res = play_message_datetime(chan, vmu, origtime, filename);
7692 if (!res)
7693 res = play_message_callerid(chan, vms, cid, context, 0);
7695 res = 't';
7696 break;
7698 case 2: /* Call back */
7700 if (ast_strlen_zero(cid))
7701 break;
7703 ast_callerid_parse(cid, &name, &num);
7704 while ((res > -1) && (res != 't')) {
7705 switch (res) {
7706 case '1':
7707 if (num) {
7708 /* Dial the CID number */
7709 res = dialout(chan, vmu, num, vmu->callback);
7710 if (res) {
7711 ast_config_destroy(msg_cfg);
7712 return 9;
7714 } else {
7715 res = '2';
7717 break;
7719 case '2':
7720 /* Want to enter a different number, can only do this if there's a dialout context for this user */
7721 if (!ast_strlen_zero(vmu->dialout)) {
7722 res = dialout(chan, vmu, NULL, vmu->dialout);
7723 if (res) {
7724 ast_config_destroy(msg_cfg);
7725 return 9;
7727 } else {
7728 if (option_verbose > 2)
7729 ast_verbose( VERBOSE_PREFIX_3 "Caller can not specify callback number - no dialout context available\n");
7730 res = ast_play_and_wait(chan, "vm-sorry");
7732 ast_config_destroy(msg_cfg);
7733 return res;
7734 case '*':
7735 res = 't';
7736 break;
7737 case '3':
7738 case '4':
7739 case '5':
7740 case '6':
7741 case '7':
7742 case '8':
7743 case '9':
7744 case '0':
7746 res = ast_play_and_wait(chan, "vm-sorry");
7747 retries++;
7748 break;
7749 default:
7750 if (num) {
7751 if (option_verbose > 2)
7752 ast_verbose( VERBOSE_PREFIX_3 "Confirm CID number '%s' is number to use for callback\n", num);
7753 res = ast_play_and_wait(chan, "vm-num-i-have");
7754 if (!res)
7755 res = play_message_callerid(chan, vms, num, vmu->context, 1);
7756 if (!res)
7757 res = ast_play_and_wait(chan, "vm-tocallnum");
7758 /* Only prompt for a caller-specified number if there is a dialout context specified */
7759 if (!ast_strlen_zero(vmu->dialout)) {
7760 if (!res)
7761 res = ast_play_and_wait(chan, "vm-calldiffnum");
7763 } else {
7764 res = ast_play_and_wait(chan, "vm-nonumber");
7765 if (!ast_strlen_zero(vmu->dialout)) {
7766 if (!res)
7767 res = ast_play_and_wait(chan, "vm-toenternumber");
7770 if (!res)
7771 res = ast_play_and_wait(chan, "vm-star-cancel");
7772 if (!res)
7773 res = ast_waitfordigit(chan, 6000);
7774 if (!res) {
7775 retries++;
7776 if (retries > 3)
7777 res = 't';
7779 break;
7782 if (res == 't')
7783 res = 0;
7784 else if (res == '*')
7785 res = -1;
7787 break;
7789 case 1: /* Reply */
7790 /* Send reply directly to sender */
7791 if (ast_strlen_zero(cid))
7792 break;
7794 ast_callerid_parse(cid, &name, &num);
7795 if (!num) {
7796 if (option_verbose > 2)
7797 ast_verbose(VERBOSE_PREFIX_3 "No CID number available, no reply sent\n");
7798 if (!res)
7799 res = ast_play_and_wait(chan, "vm-nonumber");
7800 ast_config_destroy(msg_cfg);
7801 return res;
7802 } else {
7803 if (find_user(NULL, vmu->context, num)) {
7804 struct leave_vm_options leave_options;
7805 char mailbox[AST_MAX_EXTENSION * 2 + 2];
7806 snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
7808 if (option_verbose > 2)
7809 ast_verbose(VERBOSE_PREFIX_3 "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
7811 memset(&leave_options, 0, sizeof(leave_options));
7812 leave_options.record_gain = record_gain;
7813 res = leave_voicemail(chan, mailbox, &leave_options);
7814 if (!res)
7815 res = 't';
7816 ast_config_destroy(msg_cfg);
7817 return res;
7818 } else {
7819 /* Sender has no mailbox, can't reply */
7820 if (option_verbose > 2)
7821 ast_verbose( VERBOSE_PREFIX_3 "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
7822 ast_play_and_wait(chan, "vm-nobox");
7823 res = 't';
7824 ast_config_destroy(msg_cfg);
7825 return res;
7828 res = 0;
7830 break;
7833 #ifndef IMAP_STORAGE
7834 ast_config_destroy(msg_cfg);
7836 if (!res) {
7837 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
7838 vms->heard[msg] = 1;
7839 res = wait_file(chan, vms, vms->fn);
7841 #endif
7842 return res;
7845 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
7846 int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
7847 signed char record_gain)
7849 /* Record message & let caller review or re-record it, or set options if applicable */
7850 int res = 0;
7851 int cmd = 0;
7852 int max_attempts = 3;
7853 int attempts = 0;
7854 int recorded = 0;
7855 int message_exists = 0;
7856 signed char zero_gain = 0;
7857 char *acceptdtmf = "#";
7858 char *canceldtmf = "";
7860 /* Note that urgent and private are for flagging messages as such in the future */
7862 /* barf if no pointer passed to store duration in */
7863 if (duration == NULL) {
7864 ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
7865 return -1;
7868 cmd = '3'; /* Want to start by recording */
7870 while ((cmd >= 0) && (cmd != 't')) {
7871 switch (cmd) {
7872 case '1':
7873 if (!message_exists) {
7874 /* In this case, 1 is to record a message */
7875 cmd = '3';
7876 break;
7877 } else {
7878 /* Otherwise 1 is to save the existing message */
7879 if (option_verbose > 2)
7880 ast_verbose(VERBOSE_PREFIX_3 "Saving message as is\n");
7881 ast_stream_and_wait(chan, "vm-msgsaved", chan->language, "");
7882 STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, duration, vms);
7883 DISPOSE(recordfile, -1);
7884 cmd = 't';
7885 return res;
7887 case '2':
7888 /* Review */
7889 if (option_verbose > 2)
7890 ast_verbose(VERBOSE_PREFIX_3 "Reviewing the message\n");
7891 cmd = ast_stream_and_wait(chan, recordfile, chan->language, AST_DIGIT_ANY);
7892 break;
7893 case '3':
7894 message_exists = 0;
7895 /* Record */
7896 if (recorded == 1) {
7897 if (option_verbose > 2)
7898 ast_verbose(VERBOSE_PREFIX_3 "Re-recording the message\n");
7899 } else {
7900 if (option_verbose > 2)
7901 ast_verbose(VERBOSE_PREFIX_3 "Recording the message\n");
7903 if (recorded && outsidecaller) {
7904 cmd = ast_play_and_wait(chan, INTRO);
7905 cmd = ast_play_and_wait(chan, "beep");
7907 recorded = 1;
7908 /* 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 */
7909 if (record_gain)
7910 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
7911 if (ast_test_flag(vmu, VM_OPERATOR))
7912 canceldtmf = "0";
7913 cmd = ast_play_and_record_full(chan, playfile, recordfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
7914 if (record_gain)
7915 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
7916 if (cmd == -1) {
7917 /* User has hung up, no options to give */
7918 return cmd;
7920 if (cmd == '0') {
7921 break;
7922 } else if (cmd == '*') {
7923 break;
7925 #if 0
7926 else if (vmu->review && (*duration < 5)) {
7927 /* Message is too short */
7928 if (option_verbose > 2)
7929 ast_verbose(VERBOSE_PREFIX_3 "Message too short\n");
7930 cmd = ast_play_and_wait(chan, "vm-tooshort");
7931 cmd = vm_delete(recordfile);
7932 break;
7934 else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
7935 /* Message is all silence */
7936 if (option_verbose > 2)
7937 ast_verbose(VERBOSE_PREFIX_3 "Nothing recorded\n");
7938 cmd = vm_delete(recordfile);
7939 cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
7940 if (!cmd)
7941 cmd = ast_play_and_wait(chan, "vm-speakup");
7942 break;
7944 #endif
7945 else {
7946 /* If all is well, a message exists */
7947 message_exists = 1;
7948 cmd = 0;
7950 break;
7951 case '4':
7952 case '5':
7953 case '6':
7954 case '7':
7955 case '8':
7956 case '9':
7957 case '*':
7958 case '#':
7959 cmd = ast_play_and_wait(chan, "vm-sorry");
7960 break;
7961 #if 0
7962 /* XXX Commented out for the moment because of the dangers of deleting
7963 a message while recording (can put the message numbers out of sync) */
7964 case '*':
7965 /* Cancel recording, delete message, offer to take another message*/
7966 cmd = ast_play_and_wait(chan, "vm-deleted");
7967 cmd = vm_delete(recordfile);
7968 if (outsidecaller) {
7969 res = vm_exec(chan, NULL);
7970 return res;
7972 else
7973 return 1;
7974 #endif
7975 case '0':
7976 if (!ast_test_flag(vmu, VM_OPERATOR)) {
7977 cmd = ast_play_and_wait(chan, "vm-sorry");
7978 break;
7980 if (message_exists || recorded) {
7981 cmd = ast_play_and_wait(chan, "vm-saveoper");
7982 if (!cmd)
7983 cmd = ast_waitfordigit(chan, 3000);
7984 if (cmd == '1') {
7985 ast_play_and_wait(chan, "vm-msgsaved");
7986 cmd = '0';
7987 } else {
7988 ast_play_and_wait(chan, "vm-deleted");
7989 DELETE(recordfile, -1, recordfile);
7990 cmd = '0';
7993 return cmd;
7994 default:
7995 /* If the caller is an ouside caller, and the review option is enabled,
7996 allow them to review the message, but let the owner of the box review
7997 their OGM's */
7998 if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
7999 return cmd;
8000 if (message_exists) {
8001 cmd = ast_play_and_wait(chan, "vm-review");
8003 else {
8004 cmd = ast_play_and_wait(chan, "vm-torerecord");
8005 if (!cmd)
8006 cmd = ast_waitfordigit(chan, 600);
8009 if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
8010 cmd = ast_play_and_wait(chan, "vm-reachoper");
8011 if (!cmd)
8012 cmd = ast_waitfordigit(chan, 600);
8014 #if 0
8015 if (!cmd)
8016 cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
8017 #endif
8018 if (!cmd)
8019 cmd = ast_waitfordigit(chan, 6000);
8020 if (!cmd) {
8021 attempts++;
8023 if (attempts > max_attempts) {
8024 cmd = 't';
8028 if (outsidecaller)
8029 ast_play_and_wait(chan, "vm-goodbye");
8030 if (cmd == 't')
8031 cmd = 0;
8032 return cmd;
8035 #ifdef IMAP_STORAGE
8037 static void write_file(char *filename, char *buffer, unsigned long len)
8039 FILE *output;
8041 output = fopen (filename, "w");
8042 fwrite (buffer, len, 1, output);
8043 fclose (output);
8046 void mm_searched(MAILSTREAM *stream, unsigned long number)
8048 struct vm_state *vms;
8049 char *mailbox;
8050 char *user;
8051 mailbox = stream->mailbox;
8052 user = get_user_by_mailbox(mailbox);
8054 vms = get_vm_state_by_imapuser(user,2);
8055 if (vms) {
8056 if(option_debug > 2)
8057 ast_log (LOG_DEBUG, "saving mailbox message number %lu as message %d. Interactive set to %d\n",number,vms->vmArrayIndex,vms->interactive);
8058 vms->msgArray[vms->vmArrayIndex++] = number;
8059 } else {
8060 ast_log (LOG_ERROR, "No state found.\n");
8065 /* MM display body
8066 * Accepts: BODY structure pointer
8067 * prefix string
8068 * index
8070 static void display_body(BODY *body, char *pfx, long i)
8072 char tmp[MAILTMPLEN];
8073 char *s = tmp;
8074 PARAMETER *par;
8075 PART *part; /* multipart doesn't have a row to itself */
8076 if (body->type == TYPEMULTIPART) {
8077 /* if not first time, extend prefix */
8078 if (pfx)
8079 sprintf (tmp, "%s%ld.", pfx, ++i);
8080 else
8081 tmp[0] = '\0';
8082 for (i = 0, part = body->nested.part; part; part = part->next)
8083 display_body (&part->body, tmp, i++);
8084 } else { /* non-multipart, output oneline descriptor */
8085 if (!pfx)
8086 pfx = ""; /* dummy prefix if top level */
8087 sprintf (s, " %s%ld %s", pfx, ++i, body_types[body->type]);
8088 if (body->subtype)
8089 sprintf (s += strlen (s), "/%s", body->subtype);
8090 if (body->description)
8091 sprintf (s += strlen (s), " (%s)", body->description);
8092 if ((par = body->parameter))
8094 sprintf (s += strlen (s), ";%s=%s", par->attribute, par->value);
8095 while ((par = par->next));
8096 if (body->id)
8097 sprintf (s += strlen (s), ", id = %s", body->id);
8098 switch (body->type) { /* bytes or lines depending upon body type */
8099 case TYPEMESSAGE: /* encapsulated message */
8100 case TYPETEXT: /* plain text */
8101 sprintf (s += strlen (s), " (%lu lines)", body->size.lines);
8102 break;
8103 default:
8104 sprintf (s += strlen (s), " (%lu bytes)", body->size.bytes);
8105 break;
8107 /* ast_log (LOG_NOTICE,tmp); output this line */
8108 /* encapsulated message? */
8109 if ((body->type == TYPEMESSAGE) && !strcmp (body->subtype, "RFC822") && (body = body->nested.msg->body)) {
8110 if (body->type == TYPEMULTIPART)
8111 display_body (body, pfx, i - 1);
8112 else { /* build encapsulation prefix */
8113 sprintf (tmp, "%s%ld.", pfx, i);
8114 display_body (body, tmp, (long) 0);
8120 #if 0 /*No need for this. */
8121 /* MM status report
8122 * Accepts: MAIL stream
8124 static void status(MAILSTREAM *stream)
8126 unsigned long i;
8127 char *s, date[MAILTMPLEN];
8128 THREADER *thr;
8129 AUTHENTICATOR *auth;
8130 rfc822_date (date);
8131 ast_log (LOG_NOTICE,"%s\n",date);
8132 if (stream) {
8133 if (stream->mailbox)
8134 ast_log (LOG_NOTICE," %s mailbox: %s, %lu messages, %lu recent\n",
8135 stream->dtb->name, stream->mailbox, stream->nmsgs,stream->recent);
8136 else
8137 ast_log (LOG_NOTICE,"No mailbox is open on this stream\n");
8138 if (stream->user_flags[0]) {
8139 ast_log (LOG_NOTICE,"Keywords: %s\n", stream->user_flags[0]);
8140 for (i = 1; i < NUSERFLAGS && stream->user_flags[i]; ++i)
8141 ast_log (LOG_NOTICE," %s\n", stream->user_flags[i]);
8143 if (!strcmp (stream->dtb->name, "imap")) {
8144 if (LEVELIMAP4rev1 (stream))
8145 s = "IMAP4rev1 (RFC 3501)";
8146 else if (LEVEL1730 (stream))
8147 s = "IMAP4 (RFC 1730)";
8148 else if (LEVELIMAP2bis (stream))
8149 s = "IMAP2bis";
8150 else if (LEVEL1176 (stream))
8151 s = "IMAP2 (RFC 1176)";
8152 else
8153 s = "IMAP2 (RFC 1064)";
8154 ast_log (LOG_NOTICE,"%s server %s\n", s, imap_host (stream));
8155 if (LEVELIMAP4 (stream)) {
8156 if ((i = (imap_cap(stream)->auth))) {
8157 s = "";
8158 ast_log (LOG_NOTICE,"Mutually-supported SASL mechanisms:\n");
8159 while ((auth = mail_lookup_auth (find_rightmost_bit (&i) + 1))) {
8160 ast_log (LOG_NOTICE," %s\n", auth->name);
8161 if (!strcmp (auth->name, "PLAIN"))
8162 s = "\n [LOGIN will not be listed here if PLAIN is supported]\n";
8164 ast_log (LOG_NOTICE,s);
8166 ast_log (LOG_NOTICE,"Supported standard extensions:\n");
8167 if (LEVELACL (stream))
8168 ast_log (LOG_NOTICE," Access Control lists (RFC 2086)\n");
8169 if (LEVELQUOTA (stream))
8170 ast_log (LOG_NOTICE," Quotas (RFC 2087)\n");
8171 if (LEVELLITERALPLUS (stream))
8172 ast_log (LOG_NOTICE," Non-synchronizing literals (RFC 2088)\n");
8173 if (LEVELIDLE (stream))
8174 ast_log (LOG_NOTICE," IDLE unsolicited update (RFC 2177)\n");
8175 if (LEVELMBX_REF (stream))
8176 ast_log (LOG_NOTICE," Mailbox referrals (RFC 2193)\n");
8177 if (LEVELLOG_REF (stream))
8178 ast_log (LOG_NOTICE," Login referrals (RFC 2221)\n");
8179 if (LEVELANONYMOUS (stream))
8180 ast_log (LOG_NOTICE," Anonymous access (RFC 2245)\n");
8181 if (LEVELNAMESPACE (stream))
8182 ast_log (LOG_NOTICE," Multiple namespaces (RFC 2342)\n");
8183 if (LEVELUIDPLUS (stream))
8184 ast_log (LOG_NOTICE," Extended UID behavior (RFC 2359)\n");
8185 if (LEVELSTARTTLS (stream))
8186 ast_log (LOG_NOTICE," Transport Layer Security (RFC 2595)\n");
8187 if (LEVELLOGINDISABLED (stream))
8188 ast_log (LOG_NOTICE," LOGIN command disabled (RFC 2595)\n");
8189 if (LEVELID (stream))
8190 ast_log (LOG_NOTICE," Implementation identity negotiation (RFC 2971)\n");
8191 if (LEVELCHILDREN (stream))
8192 ast_log (LOG_NOTICE," LIST children announcement (RFC 3348)\n");
8193 if (LEVELMULTIAPPEND (stream))
8194 ast_log (LOG_NOTICE," Atomic multiple APPEND (RFC 3502)\n");
8195 if (LEVELBINARY (stream))
8196 ast_log (LOG_NOTICE," Binary body content (RFC 3516)\n");
8197 ast_log (LOG_NOTICE,"Supported draft extensions:\n");
8198 if (LEVELUNSELECT (stream))
8199 ast_log (LOG_NOTICE," Mailbox unselect\n");
8200 if (LEVELSASLIR (stream))
8201 ast_log (LOG_NOTICE," SASL initial client response\n");
8202 if (LEVELSORT (stream))
8203 ast_log (LOG_NOTICE," Server-based sorting\n");
8204 if (LEVELTHREAD (stream)) {
8205 ast_log (LOG_NOTICE," Server-based threading:\n");
8206 for (thr = imap_cap(stream)->threader; thr; thr = thr->next)
8207 ast_log (LOG_NOTICE," %s\n", thr->name);
8209 if (LEVELSCAN (stream))
8210 ast_log (LOG_NOTICE," Mailbox text scan\n");
8211 if ((i = imap_cap(stream)->extlevel)) {
8212 ast_log (LOG_NOTICE,"Supported BODYSTRUCTURE extensions:\n");
8213 switch (i) {
8214 case BODYEXTLOC:
8215 ast_log (LOG_NOTICE," location\n");
8216 case BODYEXTLANG:
8217 ast_log (LOG_NOTICE," language\n");
8218 case BODYEXTDSP:
8219 ast_log (LOG_NOTICE," disposition\n");
8220 case BODYEXTMD5:
8221 ast_log (LOG_NOTICE," MD5\n");
8224 }else
8225 ast_log (LOG_NOTICE,"\n");
8229 #endif
8231 /* Interfaces to C-client */
8233 void mm_exists(MAILSTREAM * stream, unsigned long number)
8235 /* mail_ping will callback here if new mail! */
8236 if(option_debug > 3)
8237 ast_log (LOG_DEBUG, "Entering EXISTS callback for message %ld\n", number);
8238 if (number == 0) return;
8239 set_update(stream);
8243 void mm_expunged(MAILSTREAM * stream, unsigned long number)
8245 /* mail_ping will callback here if expunged mail! */
8246 if(option_debug > 3)
8247 ast_log (LOG_DEBUG, "Entering EXPUNGE callback for message %ld\n", number);
8248 if (number == 0) return;
8249 set_update(stream);
8253 void mm_flags(MAILSTREAM * stream, unsigned long number)
8255 /* mail_ping will callback here if read mail! */
8256 if(option_debug > 3)
8257 ast_log (LOG_DEBUG, "Entering FLAGS callback for message %ld\n", number);
8258 if (number == 0) return;
8259 set_update(stream);
8263 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
8265 mm_log (string, errflg);
8269 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
8271 if (delimiter == '\0') {
8272 ast_mutex_lock(&delimiter_lock);
8273 delimiter = delim;
8274 ast_mutex_unlock(&delimiter_lock);
8276 if (option_debug > 4) {
8277 ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
8278 if (attributes & LATT_NOINFERIORS)
8279 ast_log(LOG_DEBUG, "no inferiors\n");
8280 if (attributes & LATT_NOSELECT)
8281 ast_log(LOG_DEBUG, "no select\n");
8282 if (attributes & LATT_MARKED)
8283 ast_log(LOG_DEBUG, "marked\n");
8284 if (attributes & LATT_UNMARKED)
8285 ast_log(LOG_DEBUG, "unmarked\n");
8290 void mm_lsub(MAILSTREAM * stream, int delimiter, char *mailbox, long attributes)
8292 if (option_debug > 4) {
8293 ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delimiter, mailbox);
8294 if (attributes & LATT_NOINFERIORS)
8295 ast_log(LOG_DEBUG, "no inferiors\n");
8296 if (attributes & LATT_NOSELECT)
8297 ast_log(LOG_DEBUG, "no select\n");
8298 if (attributes & LATT_MARKED)
8299 ast_log(LOG_DEBUG, "marked\n");
8300 if (attributes & LATT_UNMARKED)
8301 ast_log(LOG_DEBUG, "unmarked\n");
8306 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
8308 ast_log (LOG_NOTICE," Mailbox %s", mailbox);
8309 if (status->flags & SA_MESSAGES)
8310 ast_log (LOG_NOTICE,", %lu messages", status->messages);
8311 if (status->flags & SA_RECENT)
8312 ast_log (LOG_NOTICE,", %lu recent", status->recent);
8313 if (status->flags & SA_UNSEEN)
8314 ast_log (LOG_NOTICE,", %lu unseen", status->unseen);
8315 if (status->flags & SA_UIDVALIDITY)
8316 ast_log (LOG_NOTICE,", %lu UID validity", status->uidvalidity);
8317 if (status->flags & SA_UIDNEXT)
8318 ast_log (LOG_NOTICE,", %lu next UID", status->uidnext);
8319 ast_log (LOG_NOTICE,"\n");
8323 void mm_log(char *string, long errflg)
8325 switch ((short) errflg) {
8326 case NIL:
8327 if(option_debug)
8328 ast_log(LOG_DEBUG,"IMAP Info: %s\n", string);
8329 break;
8330 case PARSE:
8331 case WARN:
8332 ast_log (LOG_WARNING,"IMAP Warning: %s\n", string);
8333 break;
8334 case ERROR:
8335 ast_log (LOG_ERROR,"IMAP Error: %s\n", string);
8336 break;
8341 void mm_dlog(char *string)
8343 ast_log (LOG_NOTICE, "%s\n", string);
8347 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
8349 struct ast_vm_user *vmu;
8351 if(option_debug > 3)
8352 ast_log(LOG_DEBUG, "Entering callback mm_login\n");
8354 ast_copy_string(user, mb->user, MAILTMPLEN);
8356 /* We should only do this when necessary */
8357 if (!ast_strlen_zero(authpassword)) {
8358 ast_copy_string(pwd, authpassword, MAILTMPLEN);
8359 } else {
8360 AST_LIST_TRAVERSE(&users, vmu, list) {
8361 if(!strcasecmp(mb->user, vmu->imapuser)) {
8362 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
8363 break;
8370 void mm_critical(MAILSTREAM * stream)
8375 void mm_nocritical(MAILSTREAM * stream)
8380 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
8382 kill (getpid (), SIGSTOP);
8383 return NIL;
8387 void mm_fatal(char *string)
8389 ast_log(LOG_ERROR,"IMAP access FATAL error: %s\n", string);
8392 /* C-client callback to handle quota */
8393 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
8395 struct vm_state *vms;
8396 char *mailbox;
8397 char *user;
8398 unsigned long usage = 0;
8399 unsigned long limit = 0;
8401 while (pquota) {
8402 usage = pquota->usage;
8403 limit = pquota->limit;
8404 pquota = pquota->next;
8407 mailbox = stream->mailbox;
8408 user = get_user_by_mailbox(mailbox);
8409 vms = get_vm_state_by_imapuser(user,2);
8410 if (vms) {
8411 if(option_debug > 2)
8412 ast_log (LOG_DEBUG, "User %s usage is %lu, limit is %lu\n",user,usage,limit);
8413 vms->quota_usage = usage;
8414 vms->quota_limit = limit;
8415 } else {
8416 ast_log (LOG_ERROR, "No state found.\n");
8420 static char *get_header_by_tag(char *header, char *tag)
8422 char *start;
8423 int taglen;
8424 char *eol_pnt;
8426 if (!header || !tag)
8427 return NULL;
8429 taglen = strlen(tag) + 1;
8430 if (taglen < 1)
8431 return NULL;
8433 start = strstr(header, tag);
8434 if (!start)
8435 return NULL;
8437 ast_mutex_lock(&imaptemp_lock);
8438 ast_copy_string(imaptemp, start+taglen, sizeof(imaptemp));
8439 ast_mutex_unlock(&imaptemp_lock);
8440 eol_pnt = strchr(imaptemp,'\n');
8441 *eol_pnt = '\0';
8442 return imaptemp;
8445 static char *get_user_by_mailbox(char *mailbox)
8447 char *start, *quote;
8448 char *eol_pnt;
8450 if (!mailbox)
8451 return NULL;
8453 start = strstr(mailbox,"user=");
8454 if (!start)
8455 return NULL;
8457 ast_mutex_lock(&imaptemp_lock);
8458 ast_copy_string(imaptemp, start+5, sizeof(imaptemp));
8459 ast_mutex_unlock(&imaptemp_lock);
8461 quote = strchr(imaptemp,'\"');
8462 if (!quote) { /* if username is not in quotes */
8463 eol_pnt = strchr(imaptemp,'/');
8464 if (!eol_pnt) {
8465 eol_pnt = strchr(imaptemp,'}');
8467 *eol_pnt = '\0';
8468 return imaptemp;
8469 } else {
8470 eol_pnt = strchr(imaptemp+1,'\"');
8471 *eol_pnt = '\0';
8472 return imaptemp+1;
8476 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
8478 struct vmstate *vlist = NULL;
8480 vlist = vmstates;
8481 while (vlist) {
8482 if (vlist->vms) {
8483 if (vlist->vms->imapuser) {
8484 if (!strcmp(vlist->vms->imapuser,user)) {
8485 if (interactive == 2) {
8486 return vlist->vms;
8487 } else if (vlist->vms->interactive == interactive) {
8488 return vlist->vms;
8491 } else {
8492 if(option_debug > 2)
8493 ast_log(LOG_DEBUG, " error: imapuser is NULL for %s\n",user);
8495 } else {
8496 if(option_debug > 2)
8497 ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",user);
8499 vlist = vlist->next;
8501 if(option_debug > 2)
8502 ast_log(LOG_DEBUG, "%s not found in vmstates\n",user);
8503 return NULL;
8506 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive)
8508 struct vmstate *vlist = NULL;
8510 vlist = vmstates;
8511 if(option_debug > 2)
8512 ast_log(LOG_DEBUG, "Mailbox set to %s\n",mailbox);
8513 while (vlist) {
8514 if (vlist->vms) {
8515 if (vlist->vms->username) {
8516 if(option_debug > 2)
8517 ast_log(LOG_DEBUG, " comparing mailbox %s (i=%d) to vmstate mailbox %s (i=%d)\n",mailbox,interactive,vlist->vms->username,vlist->vms->interactive);
8518 if (!strcmp(vlist->vms->username,mailbox) && vlist->vms->interactive == interactive) {
8519 if(option_debug > 2)
8520 ast_log(LOG_DEBUG, " Found it!\n");
8521 return vlist->vms;
8523 } else {
8524 if(option_debug > 2)
8525 ast_log(LOG_DEBUG, " error: username is NULL for %s\n",mailbox);
8527 } else {
8528 if(option_debug > 2)
8529 ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",mailbox);
8531 vlist = vlist->next;
8533 if(option_debug > 2)
8534 ast_log(LOG_DEBUG, "%s not found in vmstates\n",mailbox);
8535 return NULL;
8538 static void vmstate_insert(struct vm_state *vms)
8540 struct vmstate *v;
8541 struct vm_state *altvms;
8543 /* If interactive, it probably already exists, and we should
8544 use the one we already have since it is more up to date.
8545 We can compare the username to find the duplicate */
8546 if (vms->interactive == 1) {
8547 altvms = get_vm_state_by_mailbox(vms->username,0);
8548 if (altvms) {
8549 if(option_debug > 2)
8550 ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
8551 vms->newmessages = altvms->newmessages;
8552 vms->oldmessages = altvms->oldmessages;
8553 if(option_debug > 2)
8554 ast_log(LOG_DEBUG, "check_msgArray before memcpy\n");
8555 check_msgArray(vms);
8556 /* memcpy(vms->msgArray, altvms->msgArray, sizeof(long)*256); */
8557 copy_msgArray(vms, altvms);
8558 if(option_debug > 2)
8559 ast_log(LOG_DEBUG, "check_msgArray after memcpy\n");
8560 check_msgArray(vms);
8561 vms->vmArrayIndex = altvms->vmArrayIndex;
8562 vms->lastmsg = altvms->lastmsg;
8563 vms->curmsg = altvms->curmsg;
8564 /* get a pointer to the persistent store */
8565 vms->persist_vms = altvms;
8566 /* Reuse the mailstream? */
8567 vms->mailstream = altvms->mailstream;
8568 /* vms->mailstream = NIL; */
8572 v = (struct vmstate *)malloc(sizeof(struct vmstate));
8573 if (!v) {
8574 ast_log(LOG_ERROR, "Out of memory\n");
8576 if(option_debug > 2)
8577 ast_log(LOG_DEBUG, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
8578 ast_mutex_lock(&vmstate_lock);
8579 v->vms = vms;
8580 v->next = vmstates;
8581 vmstates = v;
8582 ast_mutex_unlock(&vmstate_lock);
8585 static void vmstate_delete(struct vm_state *vms)
8587 struct vmstate *vc, *vf = NULL, *vl = NULL;
8588 struct vm_state *altvms;
8590 /* If interactive, we should copy pertainent info
8591 back to the persistent state (to make update immediate) */
8592 if (vms->interactive == 1) {
8593 altvms = vms->persist_vms;
8594 if (altvms) {
8595 if(option_debug > 2)
8596 ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
8597 altvms->newmessages = vms->newmessages;
8598 altvms->oldmessages = vms->oldmessages;
8599 altvms->updated = 2;
8603 ast_mutex_lock(&vmstate_lock);
8604 vc = vmstates;
8605 if(option_debug > 2)
8606 ast_log(LOG_DEBUG, "Removing vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
8607 while (vc) {
8608 if (vc->vms == vms) {
8609 vf = vc;
8610 if (vl)
8611 vl->next = vc->next;
8612 else
8613 vmstates = vc->next;
8614 break;
8616 vl = vc;
8617 vc = vc->next;
8619 if (!vf) {
8620 ast_log(LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n",vms->imapuser,vms->username);
8621 } else {
8622 free(vf);
8624 ast_mutex_unlock(&vmstate_lock);
8627 static void set_update(MAILSTREAM * stream)
8629 struct vm_state *vms;
8630 char *mailbox;
8631 char *user;
8633 mailbox = stream->mailbox;
8634 user = get_user_by_mailbox(mailbox);
8635 vms = get_vm_state_by_imapuser(user, 0);
8636 if (vms) {
8637 if(option_debug > 2)
8638 ast_log (LOG_DEBUG, "User %s mailbox set for update.\n",user);
8639 vms->updated = 2; /* set updated flag since mailbox changed */
8640 } else {
8641 if(option_debug > 2)
8642 ast_log (LOG_WARNING, "User %s mailbox not found for update.\n",user);
8646 static void init_vm_state(struct vm_state *vms)
8648 int x;
8649 vms->vmArrayIndex = 0;
8650 for (x = 0; x < 256; x++) {
8651 vms->msgArray[x] = 0;
8655 static void check_msgArray(struct vm_state *vms)
8657 int x;
8658 for (x = 0; x<256; x++) {
8659 if (vms->msgArray[x]!=0) {
8660 if(option_debug)
8661 ast_log (LOG_DEBUG, "Item %d set to %ld\n",x,vms->msgArray[x]);
8666 static void copy_msgArray(struct vm_state *dst, struct vm_state *src)
8668 int x;
8669 for (x = 0; x<256; x++) {
8670 dst->msgArray[x] = src->msgArray[x];
8674 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format)
8676 char *body_content;
8677 char *body_decoded;
8678 unsigned long len;
8679 unsigned long newlen;
8680 char filename[256];
8682 if (!body || body == NIL)
8683 return -1;
8684 display_body (body, NIL, (long) 0);
8685 body_content = mail_fetchbody (vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
8686 if (body_content != NIL) {
8687 sprintf(filename,"%s.%s", vms->fn, format);
8688 /* ast_log (LOG_DEBUG,body_content); */
8689 body_decoded = rfc822_base64 ((unsigned char *)body_content, len, &newlen);
8690 write_file (filename, (char *) body_decoded, newlen);
8692 return 0;
8695 /* get delimiter via mm_list callback */
8696 static void get_mailbox_delimiter(MAILSTREAM *stream) {
8697 char tmp[50];
8698 sprintf(tmp, "{%s}", imapserver);
8699 mail_list(stream, tmp, "*");
8702 #endif /* IMAP_STORAGE */
8704 /* This is a workaround so that menuselect displays a proper description
8705 * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
8708 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
8709 .load = load_module,
8710 .unload = unload_module,
8711 .reload = reload,