00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144 #include "asterisk.h"
00145
00146 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 184728 $")
00147
00148 #include <ctype.h>
00149 #include <sys/time.h>
00150 #include <sys/stat.h>
00151 #include <sys/mman.h>
00152 #include <time.h>
00153 #include <dirent.h>
00154 #include <locale.h>
00155
00156
00157 #include "asterisk/paths.h"
00158 #include "asterisk/lock.h"
00159 #include "asterisk/file.h"
00160 #include "asterisk/channel.h"
00161 #include "asterisk/pbx.h"
00162 #include "asterisk/config.h"
00163 #include "asterisk/say.h"
00164 #include "asterisk/module.h"
00165 #include "asterisk/app.h"
00166 #include "asterisk/manager.h"
00167 #include "asterisk/dsp.h"
00168 #include "asterisk/localtime.h"
00169 #include "asterisk/cli.h"
00170 #include "asterisk/utils.h"
00171 #include "asterisk/linkedlists.h"
00172 #include "asterisk/callerid.h"
00173
00174 #ifndef TRUE
00175 #define TRUE 1
00176 #endif
00177 #ifndef FALSE
00178 #define FALSE 0
00179 #endif
00180
00181
00182 #define MVM_REVIEW (1 << 0)
00183 #define MVM_OPERATOR (1 << 1)
00184 #define MVM_REALTIME (1 << 2)
00185 #define MVM_SVMAIL (1 << 3)
00186 #define MVM_ENVELOPE (1 << 4)
00187 #define MVM_PBXSKIP (1 << 9)
00188 #define MVM_ALLOCED (1 << 13)
00189
00190
00191
00192 #define SENDMAIL "/usr/sbin/sendmail -t"
00193
00194 #define SOUND_INTRO "vm-intro"
00195 #define B64_BASEMAXINLINE 256
00196 #define B64_BASELINELEN 72
00197 #define EOL "\r\n"
00198
00199 #define MAX_DATETIME_FORMAT 512
00200 #define MAX_NUM_CID_CONTEXTS 10
00201
00202 #define ERROR_LOCK_PATH -100
00203 #define VOICEMAIL_DIR_MODE 0700
00204
00205 #define VOICEMAIL_CONFIG "minivm.conf"
00206 #define ASTERISK_USERNAME "asterisk"
00207
00208
00209 enum mvm_messagetype {
00210 MVM_MESSAGE_EMAIL,
00211 MVM_MESSAGE_PAGE
00212
00213 };
00214
00215 static char MVM_SPOOL_DIR[PATH_MAX];
00216
00217
00218 static char *app_minivm_record = "MinivmRecord";
00219 static char *app_minivm_greet = "MinivmGreet";
00220 static char *app_minivm_notify = "MinivmNotify";
00221 static char *app_minivm_delete = "MinivmDelete";
00222 static char *app_minivm_accmess = "MinivmAccMess";
00223
00224 static char *synopsis_minivm_record = "Receive Mini-Voicemail and forward via e-mail";
00225 static char *descrip_minivm_record =
00226 " MinivmRecord(username@domain[,options]):\n"
00227 "This application is part of the Mini-Voicemail system, configured in minivm.conf.\n"
00228 "MiniVM records audio file in configured format and forwards message to e-mail and pager.\n"
00229 "If there's no user account for that address, a temporary account will\n"
00230 "be used with default options.\n"
00231 "The recorded file name and path will be stored in MVM_FILENAME and the \n"
00232 "duration of the message will be stored in MVM_DURATION\n"
00233 "\nNote: If the caller hangs up after the recording, the only way to send\n"
00234 "the message and clean up is to execute in the \"h\" extension.\n"
00235 "\nThe application will exit if any of the following DTMF digits are \n"
00236 "received and the requested extension exist in the current context.\n"
00237 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
00238 " * - Jump to the 'a' extension in the current dialplan context.\n"
00239 "\n"
00240 "Result is given in channel variable MVM_RECORD_STATUS\n"
00241 " The possible values are: SUCCESS | USEREXIT | FAILED\n\n"
00242 " Options:\n"
00243 " g(#) - Use the specified amount of gain when recording the voicemail\n"
00244 " message. The units are whole-number decibels (dB).\n"
00245 "\n";
00246
00247 static char *synopsis_minivm_greet = "Play Mini-Voicemail prompts";
00248 static char *descrip_minivm_greet =
00249 " MinivmGreet(username@domain[,options]):\n"
00250 "This application is part of the Mini-Voicemail system, configured in minivm.conf.\n"
00251 "MinivmGreet() plays default prompts or user specific prompts for an account.\n"
00252 "Busy and unavailable messages can be choosen, but will be overridden if a temporary\n"
00253 "message exists for the account.\n"
00254 "\n"
00255 "Result is given in channel variable MVM_GREET_STATUS\n"
00256 " The possible values are: SUCCESS | USEREXIT | FAILED\n\n"
00257 " Options:\n"
00258 " b - Play the 'busy' greeting to the calling party.\n"
00259 " s - Skip the playback of instructions for leaving a message to the\n"
00260 " calling party.\n"
00261 " u - Play the 'unavailable greeting.\n"
00262 "\n";
00263
00264 static char *synopsis_minivm_notify = "Notify voicemail owner about new messages.";
00265 static char *descrip_minivm_notify =
00266 " MinivmNotify(username@domain[,template]):\n"
00267 "This application is part of the Mini-Voicemail system, configured in minivm.conf.\n"
00268 "MiniVMnotify forwards messages about new voicemail to e-mail and pager.\n"
00269 "If there's no user account for that address, a temporary account will\n"
00270 "be used with default options (set in minivm.conf).\n"
00271 "The recorded file name and path will be read from MVM_FILENAME and the \n"
00272 "duration of the message will be accessed from MVM_DURATION (set by MinivmRecord() )\n"
00273 "If the channel variable MVM_COUNTER is set, this will be used in the\n"
00274 "message file name and available in the template for the message.\n"
00275 "If not template is given, the default email template will be used to send email and\n"
00276 "default pager template to send paging message (if the user account is configured with\n"
00277 "a paging address.\n"
00278 "\n"
00279 "Result is given in channel variable MVM_NOTIFY_STATUS\n"
00280 " The possible values are: SUCCESS | FAILED\n"
00281 "\n";
00282
00283 static char *synopsis_minivm_delete = "Delete Mini-Voicemail voicemail messages";
00284 static char *descrip_minivm_delete =
00285 " MinivmDelete(filename):\n"
00286 "This application is part of the Mini-Voicemail system, configured in minivm.conf.\n"
00287 "It deletes voicemail file set in MVM_FILENAME or given filename.\n"
00288 "\n"
00289 "Result is given in channel variable MVM_DELETE_STATUS\n"
00290 " The possible values are: SUCCESS | FAILED\n"
00291 " FAILED is set if the file does not exist or can't be deleted.\n"
00292 "\n";
00293
00294 static char *synopsis_minivm_accmess = "Record account specific messages";
00295 static char *descrip_minivm_accmess =
00296 " MinivmAccmess(username@domain,option):\n"
00297 "This application is part of the Mini-Voicemail system, configured in minivm.conf.\n"
00298 "Use this application to record account specific audio/video messages for\n"
00299 "busy, unavailable and temporary messages.\n"
00300 "Account specific directories will be created if they do not exist.\n"
00301 "\nThe option selects message to be recorded:\n"
00302 " u Unavailable\n"
00303 " b Busy\n"
00304 " t Temporary (overrides busy and unavailable)\n"
00305 " n Account name\n"
00306 "\n"
00307 "Result is given in channel variable MVM_ACCMESS_STATUS\n"
00308 " The possible values are: SUCCESS | FAILED\n"
00309 " FAILED is set if the file can't be created.\n"
00310 "\n";
00311
00312 enum {
00313 OPT_SILENT = (1 << 0),
00314 OPT_BUSY_GREETING = (1 << 1),
00315 OPT_UNAVAIL_GREETING = (1 << 2),
00316 OPT_TEMP_GREETING = (1 << 3),
00317 OPT_NAME_GREETING = (1 << 4),
00318 OPT_RECORDGAIN = (1 << 5),
00319 } minivm_option_flags;
00320
00321 enum {
00322 OPT_ARG_RECORDGAIN = 0,
00323 OPT_ARG_ARRAY_SIZE = 1,
00324 } minivm_option_args;
00325
00326 AST_APP_OPTIONS(minivm_app_options, {
00327 AST_APP_OPTION('s', OPT_SILENT),
00328 AST_APP_OPTION('b', OPT_BUSY_GREETING),
00329 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00330 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00331 });
00332
00333 AST_APP_OPTIONS(minivm_accmess_options, {
00334 AST_APP_OPTION('b', OPT_BUSY_GREETING),
00335 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00336 AST_APP_OPTION('t', OPT_TEMP_GREETING),
00337 AST_APP_OPTION('n', OPT_NAME_GREETING),
00338 });
00339
00340
00341 struct minivm_account {
00342 char username[AST_MAX_CONTEXT];
00343 char domain[AST_MAX_CONTEXT];
00344
00345 char pincode[10];
00346 char fullname[120];
00347 char email[80];
00348 char pager[80];
00349 char accountcode[AST_MAX_ACCOUNT_CODE];
00350 char serveremail[80];
00351 char externnotify[160];
00352 char language[MAX_LANGUAGE];
00353 char zonetag[80];
00354 char uniqueid[20];
00355 char exit[80];
00356 char attachfmt[80];
00357 char etemplate[80];
00358 char ptemplate[80];
00359 unsigned int flags;
00360 struct ast_variable *chanvars;
00361 double volgain;
00362 AST_LIST_ENTRY(minivm_account) list;
00363 };
00364
00365
00366 static AST_LIST_HEAD_STATIC(minivm_accounts, minivm_account);
00367
00368
00369
00370
00371
00372 struct minivm_template {
00373 char name[80];
00374 char *body;
00375 char fromaddress[100];
00376 char serveremail[80];
00377 char subject[100];
00378 char charset[32];
00379 char locale[20];
00380 char dateformat[80];
00381 int attachment;
00382 AST_LIST_ENTRY(minivm_template) list;
00383 };
00384
00385
00386 static AST_LIST_HEAD_STATIC(message_templates, minivm_template);
00387
00388
00389 struct leave_vm_options {
00390 unsigned int flags;
00391 signed char record_gain;
00392 };
00393
00394
00395 struct b64_baseio {
00396 int iocp;
00397 int iolen;
00398 int linelength;
00399 int ateof;
00400 unsigned char iobuf[B64_BASEMAXINLINE];
00401 };
00402
00403
00404 struct minivm_zone {
00405 char name[80];
00406 char timezone[80];
00407 char msg_format[BUFSIZ];
00408 AST_LIST_ENTRY(minivm_zone) list;
00409 };
00410
00411
00412 static AST_LIST_HEAD_STATIC(minivm_zones, minivm_zone);
00413
00414
00415 struct minivm_stats {
00416 int voicemailaccounts;
00417 int timezones;
00418 int templates;
00419
00420 struct timeval reset;
00421 int receivedmessages;
00422 struct timeval lastreceived;
00423 };
00424
00425
00426 static struct minivm_stats global_stats;
00427
00428 AST_MUTEX_DEFINE_STATIC(minivmlock);
00429 AST_MUTEX_DEFINE_STATIC(minivmloglock);
00430
00431 FILE *minivmlogfile;
00432
00433 static int global_vmminmessage;
00434 static int global_vmmaxmessage;
00435 static int global_maxsilence;
00436 static int global_maxgreet;
00437 static int global_silencethreshold = 128;
00438 static char global_mailcmd[160];
00439 static char global_externnotify[160];
00440 static char global_logfile[PATH_MAX];
00441 static char default_vmformat[80];
00442
00443 static struct ast_flags globalflags = {0};
00444 static int global_saydurationminfo;
00445 static char global_charset[32];
00446
00447 static double global_volgain;
00448
00449
00450 #define DEFAULT_DATEFORMAT "%A, %B %d, %Y at %r"
00451 #define DEFAULT_CHARSET "ISO-8859-1"
00452
00453
00454 static char *message_template_parse_filebody(const char *filename);
00455 static char *message_template_parse_emailbody(const char *body);
00456 static int create_vmaccount(char *name, struct ast_variable *var, int realtime);
00457 static struct minivm_account *find_user_realtime(const char *domain, const char *username);
00458 static char *handle_minivm_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00459
00460
00461 static struct minivm_template *message_template_create(const char *name)
00462 {
00463 struct minivm_template *template;
00464
00465 template = ast_calloc(1, sizeof(*template));
00466 if (!template)
00467 return NULL;
00468
00469
00470 ast_copy_string(template->name, name, sizeof(template->name));
00471 ast_copy_string(template->dateformat, DEFAULT_DATEFORMAT, sizeof(template->dateformat));
00472 ast_copy_string(template->charset, DEFAULT_CHARSET, sizeof(template->charset));
00473 ast_copy_string(template->subject, "New message in mailbox ${MVM_USERNAME}@${MVM_DOMAIN}", sizeof(template->subject));
00474 template->attachment = TRUE;
00475
00476 return template;
00477 }
00478
00479
00480 static void message_template_free(struct minivm_template *template)
00481 {
00482 if (template->body)
00483 ast_free(template->body);
00484
00485 ast_free (template);
00486 }
00487
00488
00489 static int message_template_build(const char *name, struct ast_variable *var)
00490 {
00491 struct minivm_template *template;
00492 int error = 0;
00493
00494 template = message_template_create(name);
00495 if (!template) {
00496 ast_log(LOG_ERROR, "Out of memory, can't allocate message template object %s.\n", name);
00497 return -1;
00498 }
00499
00500 while (var) {
00501 ast_debug(3, "Configuring template option %s = \"%s\" for template %s\n", var->name, var->value, name);
00502 if (!strcasecmp(var->name, "fromaddress")) {
00503 ast_copy_string(template->fromaddress, var->value, sizeof(template->fromaddress));
00504 } else if (!strcasecmp(var->name, "fromemail")) {
00505 ast_copy_string(template->serveremail, var->value, sizeof(template->serveremail));
00506 } else if (!strcasecmp(var->name, "subject")) {
00507 ast_copy_string(template->subject, var->value, sizeof(template->subject));
00508 } else if (!strcasecmp(var->name, "locale")) {
00509 ast_copy_string(template->locale, var->value, sizeof(template->locale));
00510 } else if (!strcasecmp(var->name, "attachmedia")) {
00511 template->attachment = ast_true(var->value);
00512 } else if (!strcasecmp(var->name, "dateformat")) {
00513 ast_copy_string(template->dateformat, var->value, sizeof(template->dateformat));
00514 } else if (!strcasecmp(var->name, "charset")) {
00515 ast_copy_string(template->charset, var->value, sizeof(template->charset));
00516 } else if (!strcasecmp(var->name, "templatefile")) {
00517 if (template->body)
00518 ast_free(template->body);
00519 template->body = message_template_parse_filebody(var->value);
00520 if (!template->body) {
00521 ast_log(LOG_ERROR, "Error reading message body definition file %s\n", var->value);
00522 error++;
00523 }
00524 } else if (!strcasecmp(var->name, "messagebody")) {
00525 if (template->body)
00526 ast_free(template->body);
00527 template->body = message_template_parse_emailbody(var->value);
00528 if (!template->body) {
00529 ast_log(LOG_ERROR, "Error parsing message body definition:\n %s\n", var->value);
00530 error++;
00531 }
00532 } else {
00533 ast_log(LOG_ERROR, "Unknown message template configuration option \"%s=%s\"\n", var->name, var->value);
00534 error++;
00535 }
00536 var = var->next;
00537 }
00538 if (error)
00539 ast_log(LOG_ERROR, "-- %d errors found parsing message template definition %s\n", error, name);
00540
00541 AST_LIST_LOCK(&message_templates);
00542 AST_LIST_INSERT_TAIL(&message_templates, template, list);
00543 AST_LIST_UNLOCK(&message_templates);
00544
00545 global_stats.templates++;
00546
00547 return error;
00548 }
00549
00550
00551 static struct minivm_template *message_template_find(const char *name)
00552 {
00553 struct minivm_template *this, *res = NULL;
00554
00555 if (ast_strlen_zero(name))
00556 return NULL;
00557
00558 AST_LIST_LOCK(&message_templates);
00559 AST_LIST_TRAVERSE(&message_templates, this, list) {
00560 if (!strcasecmp(this->name, name)) {
00561 res = this;
00562 break;
00563 }
00564 }
00565 AST_LIST_UNLOCK(&message_templates);
00566
00567 return res;
00568 }
00569
00570
00571
00572 static void message_destroy_list(void)
00573 {
00574 struct minivm_template *this;
00575 AST_LIST_LOCK(&message_templates);
00576 while ((this = AST_LIST_REMOVE_HEAD(&message_templates, list)))
00577 message_template_free(this);
00578
00579 AST_LIST_UNLOCK(&message_templates);
00580 }
00581
00582
00583 static int b64_inbuf(struct b64_baseio *bio, FILE *fi)
00584 {
00585 int l;
00586
00587 if (bio->ateof)
00588 return 0;
00589
00590 if ((l = fread(bio->iobuf, 1, B64_BASEMAXINLINE,fi)) <= 0) {
00591 if (ferror(fi))
00592 return -1;
00593
00594 bio->ateof = 1;
00595 return 0;
00596 }
00597
00598 bio->iolen= l;
00599 bio->iocp= 0;
00600
00601 return 1;
00602 }
00603
00604
00605 static int b64_inchar(struct b64_baseio *bio, FILE *fi)
00606 {
00607 if (bio->iocp >= bio->iolen) {
00608 if (!b64_inbuf(bio, fi))
00609 return EOF;
00610 }
00611
00612 return bio->iobuf[bio->iocp++];
00613 }
00614
00615
00616 static int b64_ochar(struct b64_baseio *bio, int c, FILE *so)
00617 {
00618 if (bio->linelength >= B64_BASELINELEN) {
00619 if (fputs(EOL,so) == EOF)
00620 return -1;
00621
00622 bio->linelength= 0;
00623 }
00624
00625 if (putc(((unsigned char) c), so) == EOF)
00626 return -1;
00627
00628 bio->linelength++;
00629
00630 return 1;
00631 }
00632
00633
00634 static int base_encode(char *filename, FILE *so)
00635 {
00636 unsigned char dtable[B64_BASEMAXINLINE];
00637 int i,hiteof= 0;
00638 FILE *fi;
00639 struct b64_baseio bio;
00640
00641 memset(&bio, 0, sizeof(bio));
00642 bio.iocp = B64_BASEMAXINLINE;
00643
00644 if (!(fi = fopen(filename, "rb"))) {
00645 ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
00646 return -1;
00647 }
00648
00649 for (i= 0; i<9; i++) {
00650 dtable[i]= 'A'+i;
00651 dtable[i+9]= 'J'+i;
00652 dtable[26+i]= 'a'+i;
00653 dtable[26+i+9]= 'j'+i;
00654 }
00655 for (i= 0; i < 8; i++) {
00656 dtable[i+18]= 'S'+i;
00657 dtable[26+i+18]= 's'+i;
00658 }
00659 for (i= 0; i < 10; i++) {
00660 dtable[52+i]= '0'+i;
00661 }
00662 dtable[62]= '+';
00663 dtable[63]= '/';
00664
00665 while (!hiteof){
00666 unsigned char igroup[3], ogroup[4];
00667 int c,n;
00668
00669 igroup[0]= igroup[1]= igroup[2]= 0;
00670
00671 for (n= 0; n < 3; n++) {
00672 if ((c = b64_inchar(&bio, fi)) == EOF) {
00673 hiteof= 1;
00674 break;
00675 }
00676 igroup[n]= (unsigned char)c;
00677 }
00678
00679 if (n> 0) {
00680 ogroup[0]= dtable[igroup[0]>>2];
00681 ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
00682 ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
00683 ogroup[3]= dtable[igroup[2]&0x3F];
00684
00685 if (n<3) {
00686 ogroup[3]= '=';
00687
00688 if (n<2)
00689 ogroup[2]= '=';
00690 }
00691
00692 for (i= 0;i<4;i++)
00693 b64_ochar(&bio, ogroup[i], so);
00694 }
00695 }
00696
00697
00698 if (fputs(EOL, so) == EOF)
00699 return 0;
00700
00701 fclose(fi);
00702
00703 return 1;
00704 }
00705
00706 static int get_date(char *s, int len)
00707 {
00708 struct ast_tm tm;
00709 struct timeval now = ast_tvnow();
00710
00711 ast_localtime(&now, &tm, NULL);
00712 return ast_strftime(s, len, "%a %b %e %r %Z %Y", &tm);
00713 }
00714
00715
00716
00717 static void free_user(struct minivm_account *vmu)
00718 {
00719 if (vmu->chanvars)
00720 ast_variables_destroy(vmu->chanvars);
00721 ast_free(vmu);
00722 }
00723
00724
00725
00726
00727
00728
00729 static void prep_email_sub_vars(struct ast_channel *channel, const struct minivm_account *vmu, const char *cidnum, const char *cidname, const char *dur, const char *date, const char *counter)
00730 {
00731 char callerid[256];
00732 struct ast_variable *var;
00733
00734 if (!channel) {
00735 ast_log(LOG_ERROR, "No allocated channel, giving up...\n");
00736 return;
00737 }
00738
00739 for (var = vmu->chanvars ; var ; var = var->next) {
00740 pbx_builtin_setvar_helper(channel, var->name, var->value);
00741 }
00742
00743
00744 pbx_builtin_setvar_helper(channel, "MVM_NAME", vmu->fullname);
00745 pbx_builtin_setvar_helper(channel, "MVM_DUR", dur);
00746 pbx_builtin_setvar_helper(channel, "MVM_DOMAIN", vmu->domain);
00747 pbx_builtin_setvar_helper(channel, "MVM_USERNAME", vmu->username);
00748 pbx_builtin_setvar_helper(channel, "MVM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
00749 pbx_builtin_setvar_helper(channel, "MVM_CIDNAME", (cidname ? cidname : "an unknown caller"));
00750 pbx_builtin_setvar_helper(channel, "MVM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
00751 pbx_builtin_setvar_helper(channel, "MVM_DATE", date);
00752 if (!ast_strlen_zero(counter))
00753 pbx_builtin_setvar_helper(channel, "MVM_COUNTER", counter);
00754 }
00755
00756
00757 static void populate_defaults(struct minivm_account *vmu)
00758 {
00759 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
00760 ast_copy_string(vmu->attachfmt, default_vmformat, sizeof(vmu->attachfmt));
00761 vmu->volgain = global_volgain;
00762 }
00763
00764
00765 static char *mailheader_quote(const char *from, char *to, size_t len)
00766 {
00767 char *ptr = to;
00768 *ptr++ = '"';
00769 for (; ptr < to + len - 1; from++) {
00770 if (*from == '"')
00771 *ptr++ = '\\';
00772 else if (*from == '\0')
00773 break;
00774 *ptr++ = *from;
00775 }
00776 if (ptr < to + len - 1)
00777 *ptr++ = '"';
00778 *ptr = '\0';
00779 return to;
00780 }
00781
00782
00783
00784 static struct minivm_account *mvm_user_alloc(void)
00785 {
00786 struct minivm_account *new;
00787
00788 new = ast_calloc(1, sizeof(*new));
00789 if (!new)
00790 return NULL;
00791 populate_defaults(new);
00792
00793 return new;
00794 }
00795
00796
00797
00798 static void vmaccounts_destroy_list(void)
00799 {
00800 struct minivm_account *this;
00801 AST_LIST_LOCK(&minivm_accounts);
00802 while ((this = AST_LIST_REMOVE_HEAD(&minivm_accounts, list)))
00803 ast_free(this);
00804 AST_LIST_UNLOCK(&minivm_accounts);
00805 }
00806
00807
00808
00809 static struct minivm_account *find_account(const char *domain, const char *username, int createtemp)
00810 {
00811 struct minivm_account *vmu = NULL, *cur;
00812
00813
00814 if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
00815 ast_log(LOG_NOTICE, "No username or domain? \n");
00816 return NULL;
00817 }
00818 ast_debug(3, "Looking for voicemail user %s in domain %s\n", username, domain);
00819
00820 AST_LIST_LOCK(&minivm_accounts);
00821 AST_LIST_TRAVERSE(&minivm_accounts, cur, list) {
00822
00823 if (!strcasecmp(domain, cur->domain) && !strcasecmp(username, cur->username))
00824 break;
00825 }
00826 AST_LIST_UNLOCK(&minivm_accounts);
00827
00828 if (cur) {
00829 ast_debug(3, "Found account for %s@%s\n", username, domain);
00830 vmu = cur;
00831
00832 } else
00833 vmu = find_user_realtime(domain, username);
00834
00835 if (createtemp && !vmu) {
00836
00837 vmu = mvm_user_alloc();
00838 ast_set2_flag(vmu, TRUE, MVM_ALLOCED);
00839 if (vmu) {
00840 ast_copy_string(vmu->username, username, sizeof(vmu->username));
00841 ast_copy_string(vmu->domain, domain, sizeof(vmu->domain));
00842 ast_debug(1, "Created temporary account\n");
00843 }
00844
00845 }
00846 return vmu;
00847 }
00848
00849
00850
00851
00852 static struct minivm_account *find_user_realtime(const char *domain, const char *username)
00853 {
00854 struct ast_variable *var;
00855 struct minivm_account *retval;
00856 char name[MAXHOSTNAMELEN];
00857
00858 retval = mvm_user_alloc();
00859 if (!retval)
00860 return NULL;
00861
00862 if (username)
00863 ast_copy_string(retval->username, username, sizeof(retval->username));
00864
00865 populate_defaults(retval);
00866 var = ast_load_realtime("minivm", "username", username, "domain", domain, SENTINEL);
00867
00868 if (!var) {
00869 ast_free(retval);
00870 return NULL;
00871 }
00872
00873 snprintf(name, sizeof(name), "%s@%s", username, domain);
00874 create_vmaccount(name, var, TRUE);
00875
00876 ast_variables_destroy(var);
00877 return retval;
00878 }
00879
00880
00881 static int sendmail(struct minivm_template *template, struct minivm_account *vmu, char *cidnum, char *cidname, const char *filename, char *format, int duration, int attach_user_voicemail, enum mvm_messagetype type, const char *counter)
00882 {
00883 FILE *p = NULL;
00884 int pfd;
00885 char email[256] = "";
00886 char who[256] = "";
00887 char date[256];
00888 char bound[256];
00889 char fname[PATH_MAX];
00890 char dur[PATH_MAX];
00891 char tmp[80] = "/tmp/astmail-XXXXXX";
00892 char tmp2[PATH_MAX];
00893 struct timeval now;
00894 struct ast_tm tm;
00895 struct minivm_zone *the_zone = NULL;
00896 int len_passdata;
00897 struct ast_channel *ast;
00898 char *finalfilename;
00899 char *passdata = NULL;
00900 char *passdata2 = NULL;
00901 char *fromaddress;
00902 char *fromemail;
00903
00904 if (type == MVM_MESSAGE_EMAIL) {
00905 if (vmu && !ast_strlen_zero(vmu->email)) {
00906 ast_copy_string(email, vmu->email, sizeof(email));
00907 } else if (!ast_strlen_zero(vmu->username) && !ast_strlen_zero(vmu->domain))
00908 snprintf(email, sizeof(email), "%s@%s", vmu->username, vmu->domain);
00909 } else if (type == MVM_MESSAGE_PAGE) {
00910 ast_copy_string(email, vmu->pager, sizeof(email));
00911 }
00912
00913 if (ast_strlen_zero(email)) {
00914 ast_log(LOG_WARNING, "No address to send message to.\n");
00915 return -1;
00916 }
00917
00918 ast_debug(3, "Sending mail to %s@%s - Using template %s\n", vmu->username, vmu->domain, template->name);
00919
00920 if (!strcmp(format, "wav49"))
00921 format = "WAV";
00922
00923
00924
00925 if (type == MVM_MESSAGE_EMAIL && (vmu->volgain < -.001 || vmu->volgain > .001) ) {
00926 char newtmp[PATH_MAX];
00927 char tmpcmd[PATH_MAX];
00928 int tmpfd;
00929
00930 ast_copy_string(newtmp, "/tmp/XXXXXX", sizeof(newtmp));
00931 ast_debug(3, "newtmp: %s\n", newtmp);
00932 tmpfd = mkstemp(newtmp);
00933 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, filename, format, newtmp, format);
00934 ast_safe_system(tmpcmd);
00935 finalfilename = newtmp;
00936 ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", filename, format, vmu->volgain, vmu->username);
00937 } else {
00938 finalfilename = ast_strdupa(filename);
00939 }
00940
00941
00942 snprintf(fname, sizeof(fname), "%s.%s", finalfilename, format);
00943
00944 if (template->attachment)
00945 ast_debug(1, "Attaching file '%s', format '%s', uservm is '%d'\n", finalfilename, format, attach_user_voicemail);
00946
00947
00948
00949 pfd = mkstemp(tmp);
00950 if (pfd > -1) {
00951 p = fdopen(pfd, "w");
00952 if (!p) {
00953 close(pfd);
00954 pfd = -1;
00955 }
00956 ast_debug(1, "Opening temp file for e-mail: %s\n", tmp);
00957 }
00958 if (!p) {
00959 ast_log(LOG_WARNING, "Unable to open temporary file '%s'\n", tmp);
00960 return -1;
00961 }
00962
00963 ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "%s", "");
00964
00965
00966 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
00967
00968
00969 if (!ast_strlen_zero(vmu->zonetag)) {
00970
00971 struct minivm_zone *z;
00972 AST_LIST_LOCK(&minivm_zones);
00973 AST_LIST_TRAVERSE(&minivm_zones, z, list) {
00974 if (strcmp(z->name, vmu->zonetag))
00975 continue;
00976 the_zone = z;
00977 }
00978 AST_LIST_UNLOCK(&minivm_zones);
00979 }
00980
00981 now = ast_tvnow();
00982 ast_localtime(&now, &tm, the_zone ? the_zone->timezone : NULL);
00983 ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
00984
00985
00986 fprintf(p, "Date: %s\n", date);
00987
00988
00989 ast_strftime(date, sizeof(date), template->dateformat, &tm);
00990
00991
00992
00993 prep_email_sub_vars(ast, vmu, cidnum, cidname, dur, date, counter);
00994
00995
00996
00997 fromemail = ast_strlen_zero(vmu->serveremail) ? template->serveremail : vmu->serveremail;
00998
00999
01000 fromaddress = ast_strlen_zero(template->fromaddress) ? "" : template->fromaddress;
01001
01002
01003 if (ast_strlen_zero(fromemail))
01004 fromemail = "asterisk";
01005
01006 if (strchr(fromemail, '@'))
01007 ast_copy_string(who, fromemail, sizeof(who));
01008 else {
01009 char host[MAXHOSTNAMELEN];
01010 gethostname(host, sizeof(host)-1);
01011 snprintf(who, sizeof(who), "%s@%s", fromemail, host);
01012 }
01013
01014 if (ast_strlen_zero(fromaddress)) {
01015 fprintf(p, "From: Asterisk PBX <%s>\n", who);
01016 } else {
01017
01018 int vmlen = strlen(fromaddress) * 3 + 200;
01019
01020 ast_debug(4, "Fromaddress template: %s\n", fromaddress);
01021 if ((passdata = alloca(vmlen))) {
01022 pbx_substitute_variables_helper(ast, fromaddress, passdata, vmlen);
01023 len_passdata = strlen(passdata) * 2 + 3;
01024 passdata2 = alloca(len_passdata);
01025 fprintf(p, "From: %s <%s>\n", mailheader_quote(passdata, passdata2, len_passdata), who);
01026 } else {
01027 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
01028 fclose(p);
01029 return -1;
01030 }
01031 }
01032 ast_debug(4, "Fromstring now: %s\n", ast_strlen_zero(passdata) ? "-default-" : passdata);
01033
01034 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d-%s>\n", (unsigned int)ast_random(), vmu->username, (int)getpid(), who);
01035 len_passdata = strlen(vmu->fullname) * 2 + 3;
01036 passdata2 = alloca(len_passdata);
01037 if (!ast_strlen_zero(vmu->email))
01038 fprintf(p, "To: %s <%s>\n", mailheader_quote(vmu->fullname, passdata2, len_passdata), vmu->email);
01039 else
01040 fprintf(p, "To: %s <%s@%s>\n", mailheader_quote(vmu->fullname, passdata2, len_passdata), vmu->username, vmu->domain);
01041
01042 if (!ast_strlen_zero(template->subject)) {
01043 char *pass_data;
01044 int vmlen = strlen(template->subject) * 3 + 200;
01045 if ((pass_data = alloca(vmlen))) {
01046 pbx_substitute_variables_helper(ast, template->subject, pass_data, vmlen);
01047 fprintf(p, "Subject: %s\n", pass_data);
01048 } else {
01049 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
01050 fclose(p);
01051 return -1;
01052 }
01053
01054 ast_debug(4, "Subject now: %s\n", pass_data);
01055
01056 } else {
01057 fprintf(p, "Subject: New message in mailbox %s@%s\n", vmu->username, vmu->domain);
01058 ast_debug(1, "Using default subject for this email \n");
01059 }
01060
01061
01062 if (option_debug > 2)
01063 fprintf(p, "X-Asterisk-debug: template %s user account %s@%s\n", template->name, vmu->username, vmu->domain);
01064 fprintf(p, "MIME-Version: 1.0\n");
01065
01066
01067 snprintf(bound, sizeof(bound), "voicemail_%s%d%d", vmu->username, (int)getpid(), (unsigned int)ast_random());
01068
01069 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
01070
01071 fprintf(p, "--%s\n", bound);
01072 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", global_charset);
01073 if (!ast_strlen_zero(template->body)) {
01074 char *pass_data;
01075 int vmlen = strlen(template->body)*3 + 200;
01076 if ((pass_data = alloca(vmlen))) {
01077 pbx_substitute_variables_helper(ast, template->body, pass_data, vmlen);
01078 ast_debug(3, "Message now: %s\n-----\n", pass_data);
01079 fprintf(p, "%s\n", pass_data);
01080 } else
01081 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
01082 } else {
01083 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message \n"
01084
01085 "in mailbox %s from %s, on %s so you might\n"
01086 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
01087 dur, vmu->username, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
01088 ast_debug(3, "Using default message body (no template)\n-----\n");
01089 }
01090
01091 if (template->attachment) {
01092 char *ctype = "audio/x-";
01093 ast_debug(3, "Attaching file to message: %s\n", fname);
01094 if (!strcasecmp(format, "ogg"))
01095 ctype = "application/";
01096
01097 fprintf(p, "--%s\n", bound);
01098 fprintf(p, "Content-Type: %s%s; name=\"voicemailmsg.%s\"\n", ctype, format, format);
01099 fprintf(p, "Content-Transfer-Encoding: base64\n");
01100 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
01101 fprintf(p, "Content-Disposition: attachment; filename=\"voicemail%s.%s\"\n\n", counter ? counter : "", format);
01102
01103 base_encode(fname, p);
01104 fprintf(p, "\n\n--%s--\n.\n", bound);
01105 }
01106 fclose(p);
01107 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", global_mailcmd, tmp, tmp);
01108 ast_safe_system(tmp2);
01109 ast_debug(1, "Sent message to %s with command '%s' - %s\n", vmu->email, global_mailcmd, template->attachment ? "(media attachment)" : "");
01110 ast_debug(3, "Actual command used: %s\n", tmp2);
01111 if (ast)
01112 ast_channel_free(ast);
01113 return 0;
01114 }
01115
01116
01117 static int make_dir(char *dest, int len, const char *domain, const char *username, const char *folder)
01118 {
01119 return snprintf(dest, len, "%s%s/%s%s%s", MVM_SPOOL_DIR, domain, username, ast_strlen_zero(folder) ? "" : "/", folder ? folder : "");
01120 }
01121
01122
01123
01124
01125
01126
01127
01128
01129
01130 static int check_dirpath(char *dest, int len, char *domain, char *username, char *folder)
01131 {
01132 struct stat filestat;
01133 make_dir(dest, len, domain, username, folder ? folder : "");
01134 if (stat(dest, &filestat)== -1)
01135 return FALSE;
01136 else
01137 return TRUE;
01138 }
01139
01140
01141
01142
01143
01144
01145
01146
01147
01148 static int create_dirpath(char *dest, int len, char *domain, char *username, char *folder)
01149 {
01150 int res;
01151 make_dir(dest, len, domain, username, folder);
01152 if ((res = ast_mkdir(dest, 0777))) {
01153 ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01154 return -1;
01155 }
01156 ast_debug(2, "Creating directory for %s@%s folder %s : %s\n", username, domain, folder, dest);
01157 return 0;
01158 }
01159
01160
01161
01162
01163 static int invent_message(struct ast_channel *chan, char *domain, char *username, int busy, char *ecodes)
01164 {
01165 int res;
01166 char fn[PATH_MAX];
01167
01168 ast_debug(2, "Still preparing to play message ...\n");
01169
01170 snprintf(fn, sizeof(fn), "%s%s/%s/greet", MVM_SPOOL_DIR, domain, username);
01171
01172 if (ast_fileexists(fn, NULL, NULL) > 0) {
01173 res = ast_streamfile(chan, fn, chan->language);
01174 if (res)
01175 return -1;
01176 res = ast_waitstream(chan, ecodes);
01177 if (res)
01178 return res;
01179 } else {
01180 int numericusername = 1;
01181 char *i = username;
01182
01183 ast_debug(2, "No personal prompts. Using default prompt set for language\n");
01184
01185 while (*i) {
01186 ast_debug(2, "Numeric? Checking %c\n", *i);
01187 if (!isdigit(*i)) {
01188 numericusername = FALSE;
01189 break;
01190 }
01191 i++;
01192 }
01193
01194 if (numericusername) {
01195 if(ast_streamfile(chan, "vm-theperson", chan->language))
01196 return -1;
01197 if ((res = ast_waitstream(chan, ecodes)))
01198 return res;
01199
01200 res = ast_say_digit_str(chan, username, ecodes, chan->language);
01201 if (res)
01202 return res;
01203 } else {
01204 if(ast_streamfile(chan, "vm-theextensionis", chan->language))
01205 return -1;
01206 if ((res = ast_waitstream(chan, ecodes)))
01207 return res;
01208 }
01209 }
01210
01211 res = ast_streamfile(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language);
01212 if (res)
01213 return -1;
01214 res = ast_waitstream(chan, ecodes);
01215 return res;
01216 }
01217
01218
01219 static int vm_delete(char *file)
01220 {
01221 int res;
01222
01223 ast_debug(1, "Deleting voicemail file %s\n", file);
01224
01225 res = unlink(file);
01226 res |= ast_filedelete(file, NULL);
01227 return res;
01228 }
01229
01230
01231
01232 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
01233 int outsidecaller, struct minivm_account *vmu, int *duration, const char *unlockdir,
01234 signed char record_gain)
01235 {
01236 int cmd = 0;
01237 int max_attempts = 3;
01238 int attempts = 0;
01239 int recorded = 0;
01240 int message_exists = 0;
01241 signed char zero_gain = 0;
01242 char *acceptdtmf = "#";
01243 char *canceldtmf = "";
01244
01245
01246
01247
01248 if (duration == NULL) {
01249 ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
01250 return -1;
01251 }
01252
01253 cmd = '3';
01254
01255 while ((cmd >= 0) && (cmd != 't')) {
01256 switch (cmd) {
01257 case '1':
01258 ast_verb(3, "Saving message as is\n");
01259 ast_stream_and_wait(chan, "vm-msgsaved", "");
01260 cmd = 't';
01261 break;
01262 case '2':
01263
01264 ast_verb(3, "Reviewing the message\n");
01265 ast_streamfile(chan, recordfile, chan->language);
01266 cmd = ast_waitstream(chan, AST_DIGIT_ANY);
01267 break;
01268 case '3':
01269 message_exists = 0;
01270
01271 if (recorded == 1)
01272 ast_verb(3, "Re-recording the message\n");
01273 else
01274 ast_verb(3, "Recording the message\n");
01275 if (recorded && outsidecaller)
01276 cmd = ast_play_and_wait(chan, "beep");
01277 recorded = 1;
01278
01279 if (record_gain)
01280 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
01281 if (ast_test_flag(vmu, MVM_OPERATOR))
01282 canceldtmf = "0";
01283 cmd = ast_play_and_record_full(chan, playfile, recordfile, maxtime, fmt, duration, global_silencethreshold, global_maxsilence, unlockdir, acceptdtmf, canceldtmf);
01284 if (record_gain)
01285 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
01286 if (cmd == -1)
01287 return cmd;
01288 if (cmd == '0')
01289 break;
01290 else if (cmd == '*')
01291 break;
01292 else {
01293
01294 message_exists = 1;
01295 cmd = 0;
01296 }
01297 break;
01298 case '4':
01299 case '5':
01300 case '6':
01301 case '7':
01302 case '8':
01303 case '9':
01304 case '*':
01305 case '#':
01306 cmd = ast_play_and_wait(chan, "vm-sorry");
01307 break;
01308 case '0':
01309 if(!ast_test_flag(vmu, MVM_OPERATOR)) {
01310 cmd = ast_play_and_wait(chan, "vm-sorry");
01311 break;
01312 }
01313 if (message_exists || recorded) {
01314 cmd = ast_play_and_wait(chan, "vm-saveoper");
01315 if (!cmd)
01316 cmd = ast_waitfordigit(chan, 3000);
01317 if (cmd == '1') {
01318 ast_play_and_wait(chan, "vm-msgsaved");
01319 cmd = '0';
01320 } else {
01321 ast_play_and_wait(chan, "vm-deleted");
01322 vm_delete(recordfile);
01323 cmd = '0';
01324 }
01325 }
01326 return cmd;
01327 default:
01328
01329
01330
01331 if (outsidecaller && !ast_test_flag(vmu, MVM_REVIEW))
01332 return cmd;
01333 if (message_exists) {
01334 cmd = ast_play_and_wait(chan, "vm-review");
01335 } else {
01336 cmd = ast_play_and_wait(chan, "vm-torerecord");
01337 if (!cmd)
01338 cmd = ast_waitfordigit(chan, 600);
01339 }
01340
01341 if (!cmd && outsidecaller && ast_test_flag(vmu, MVM_OPERATOR)) {
01342 cmd = ast_play_and_wait(chan, "vm-reachoper");
01343 if (!cmd)
01344 cmd = ast_waitfordigit(chan, 600);
01345 }
01346 if (!cmd)
01347 cmd = ast_waitfordigit(chan, 6000);
01348 if (!cmd) {
01349 attempts++;
01350 }
01351 if (attempts > max_attempts) {
01352 cmd = 't';
01353 }
01354 }
01355 }
01356 if (outsidecaller)
01357 ast_play_and_wait(chan, "vm-goodbye");
01358 if (cmd == 't')
01359 cmd = 0;
01360 return cmd;
01361 }
01362
01363
01364 static void run_externnotify(struct ast_channel *chan, struct minivm_account *vmu)
01365 {
01366 char arguments[BUFSIZ];
01367
01368 if (ast_strlen_zero(vmu->externnotify) && ast_strlen_zero(global_externnotify))
01369 return;
01370
01371 snprintf(arguments, sizeof(arguments), "%s %s@%s %s %s&",
01372 ast_strlen_zero(vmu->externnotify) ? global_externnotify : vmu->externnotify,
01373 vmu->username, vmu->domain,
01374 chan->cid.cid_name, chan->cid.cid_num);
01375
01376 ast_debug(1, "Executing: %s\n", arguments);
01377 ast_safe_system(arguments);
01378 }
01379
01380
01381 static int notify_new_message(struct ast_channel *chan, const char *templatename, struct minivm_account *vmu, const char *filename, long duration, const char *format, char *cidnum, char *cidname)
01382 {
01383 char *stringp;
01384 struct minivm_template *etemplate;
01385 char *messageformat;
01386 int res = 0;
01387 char oldlocale[100];
01388 const char *counter;
01389
01390 if (!ast_strlen_zero(vmu->attachfmt)) {
01391 if (strstr(format, vmu->attachfmt)) {
01392 format = vmu->attachfmt;
01393 } else
01394 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, format, vmu->username, vmu->domain);
01395 }
01396
01397 etemplate = message_template_find(vmu->etemplate);
01398 if (!etemplate)
01399 etemplate = message_template_find(templatename);
01400 if (!etemplate)
01401 etemplate = message_template_find("email-default");
01402
01403
01404 stringp = messageformat = ast_strdupa(format);
01405 strsep(&stringp, "|");
01406
01407 if (!ast_strlen_zero(etemplate->locale)) {
01408 char *new_locale;
01409 ast_copy_string(oldlocale, setlocale(LC_TIME, NULL), sizeof(oldlocale));
01410 ast_debug(2, "Changing locale from %s to %s\n", oldlocale, etemplate->locale);
01411 new_locale = setlocale(LC_TIME, etemplate->locale);
01412 if (new_locale == NULL) {
01413 ast_log(LOG_WARNING, "-_-_- Changing to new locale did not work. Locale: %s\n", etemplate->locale);
01414 }
01415 }
01416
01417
01418
01419
01420 ast_channel_lock(chan);
01421 if ((counter = pbx_builtin_getvar_helper(chan, "MVM_COUNTER"))) {
01422 counter = ast_strdupa(counter);
01423 }
01424 ast_channel_unlock(chan);
01425
01426 if (ast_strlen_zero(counter)) {
01427 ast_debug(2, "MVM_COUNTER not found\n");
01428 } else {
01429 ast_debug(2, "MVM_COUNTER found - will use it with value %s\n", counter);
01430 }
01431
01432 res = sendmail(etemplate, vmu, cidnum, cidname, filename, messageformat, duration, etemplate->attachment, MVM_MESSAGE_EMAIL, counter);
01433
01434 if (res == 0 && !ast_strlen_zero(vmu->pager)) {
01435
01436 etemplate = message_template_find(vmu->ptemplate);
01437 if (!etemplate)
01438 etemplate = message_template_find("pager-default");
01439 if (etemplate->locale) {
01440 ast_copy_string(oldlocale, setlocale(LC_TIME, ""), sizeof(oldlocale));
01441 setlocale(LC_TIME, etemplate->locale);
01442 }
01443
01444 res = sendmail(etemplate, vmu, cidnum, cidname, filename, messageformat, duration, etemplate->attachment, MVM_MESSAGE_PAGE, counter);
01445 }
01446
01447 manager_event(EVENT_FLAG_CALL, "MiniVoiceMail", "Action: SentNotification\rn\nMailbox: %s@%s\r\nCounter: %s\r\n", vmu->username, vmu->domain, counter);
01448
01449 run_externnotify(chan, vmu);
01450
01451 if (etemplate->locale)
01452 setlocale(LC_TIME, oldlocale);
01453 return res;
01454 }
01455
01456
01457
01458 static int leave_voicemail(struct ast_channel *chan, char *username, struct leave_vm_options *options)
01459 {
01460 char tmptxtfile[PATH_MAX];
01461 char callerid[256];
01462 FILE *txt;
01463 int res = 0, txtdes;
01464 int msgnum;
01465 int duration = 0;
01466 char date[256];
01467 char tmpdir[PATH_MAX];
01468 char ext_context[256] = "";
01469 char fmt[80];
01470 char *domain;
01471 char tmp[256] = "";
01472 struct minivm_account *vmu;
01473 int userdir;
01474
01475 ast_copy_string(tmp, username, sizeof(tmp));
01476 username = tmp;
01477 domain = strchr(tmp, '@');
01478 if (domain) {
01479 *domain = '\0';
01480 domain++;
01481 }
01482
01483 if (!(vmu = find_account(domain, username, TRUE))) {
01484
01485 ast_log(LOG_ERROR, "Can't allocate temporary account for '%s@%s'\n", username, domain);
01486 pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "FAILED");
01487 return 0;
01488 }
01489
01490
01491 if (strcmp(vmu->domain, "localhost"))
01492 snprintf(ext_context, sizeof(ext_context), "%s@%s", username, vmu->domain);
01493 else
01494 ast_copy_string(ext_context, vmu->domain, sizeof(ext_context));
01495
01496
01497 if (ast_strlen_zero(vmu->attachfmt))
01498 ast_copy_string(fmt, default_vmformat, sizeof(fmt));
01499 else
01500 ast_copy_string(fmt, vmu->attachfmt, sizeof(fmt));
01501
01502 if (ast_strlen_zero(fmt)) {
01503 ast_log(LOG_WARNING, "No format for saving voicemail? Default %s\n", default_vmformat);
01504 pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "FAILED");
01505 return res;
01506 }
01507 msgnum = 0;
01508
01509 userdir = check_dirpath(tmpdir, sizeof(tmpdir), vmu->domain, username, "tmp");
01510
01511
01512 if (!userdir) {
01513 create_dirpath(tmpdir, sizeof(tmpdir), "0000_minivm_temp", "mediafiles", "");
01514 ast_debug(3, "Creating temporary directory %s\n", tmpdir);
01515 }
01516
01517
01518 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
01519
01520
01521
01522 txtdes = mkstemp(tmptxtfile);
01523 if (txtdes < 0) {
01524 ast_log(LOG_ERROR, "Unable to create message file %s: %s\n", tmptxtfile, strerror(errno));
01525 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
01526 if (!res)
01527 res = ast_waitstream(chan, "");
01528 pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "FAILED");
01529 return res;
01530 }
01531
01532 if (res >= 0) {
01533
01534 res = ast_streamfile(chan, "beep", chan->language);
01535 if (!res)
01536 res = ast_waitstream(chan, "");
01537 }
01538
01539
01540
01541 ast_debug(2, "Open file for metadata: %s\n", tmptxtfile);
01542
01543 res = play_record_review(chan, NULL, tmptxtfile, global_vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain);
01544
01545 txt = fdopen(txtdes, "w+");
01546 if (!txt) {
01547 ast_log(LOG_WARNING, "Error opening text file for output\n");
01548 } else {
01549 struct ast_tm tm;
01550 struct timeval now = ast_tvnow();
01551 char timebuf[30];
01552 char logbuf[BUFSIZ];
01553 get_date(date, sizeof(date));
01554 ast_localtime(&now, &tm, NULL);
01555 ast_strftime(timebuf, sizeof(timebuf), "%H:%M:%S", &tm);
01556
01557 snprintf(logbuf, sizeof(logbuf),
01558
01559 "%s:%s:%s:%s:%d:%s:%s:%s:%s:%d:%s:%s\n",
01560 username,
01561 chan->context,
01562 chan->macrocontext,
01563 chan->exten,
01564 chan->priority,
01565 chan->name,
01566 ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
01567 date,
01568 timebuf,
01569 duration,
01570 duration < global_vmminmessage ? "IGNORED" : "OK",
01571 vmu->accountcode
01572 );
01573 fprintf(txt, "%s", logbuf);
01574 if (minivmlogfile) {
01575 ast_mutex_lock(&minivmloglock);
01576 fprintf(minivmlogfile, "%s", logbuf);
01577 ast_mutex_unlock(&minivmloglock);
01578 }
01579
01580 if (duration < global_vmminmessage) {
01581 ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, global_vmminmessage);
01582 fclose(txt);
01583 ast_filedelete(tmptxtfile, NULL);
01584 unlink(tmptxtfile);
01585 pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "FAILED");
01586 return 0;
01587 }
01588 fclose(txt);
01589 if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
01590 ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
01591 unlink(tmptxtfile);
01592 pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "FAILED");
01593 if(ast_test_flag(vmu, MVM_ALLOCED))
01594 free_user(vmu);
01595 return 0;
01596 }
01597
01598
01599 pbx_builtin_setvar_helper(chan, "MVM_FILENAME", tmptxtfile);
01600 snprintf(timebuf, sizeof(timebuf), "%d", duration);
01601 pbx_builtin_setvar_helper(chan, "MVM_DURATION", timebuf);
01602 pbx_builtin_setvar_helper(chan, "MVM_FORMAT", fmt);
01603
01604 }
01605 global_stats.lastreceived = ast_tvnow();
01606 global_stats.receivedmessages++;
01607
01608
01609
01610
01611
01612
01613
01614 if (res > 0)
01615 res = 0;
01616
01617 if(ast_test_flag(vmu, MVM_ALLOCED))
01618 free_user(vmu);
01619
01620 pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "SUCCESS");
01621 return res;
01622 }
01623
01624
01625 static int minivm_notify_exec(struct ast_channel *chan, void *data)
01626 {
01627 int argc;
01628 char *argv[2];
01629 int res = 0;
01630 char tmp[PATH_MAX];
01631 char *domain;
01632 char *tmpptr;
01633 struct minivm_account *vmu;
01634 char *username = argv[0];
01635 const char *template = "";
01636 const char *filename;
01637 const char *format;
01638 const char *duration_string;
01639
01640 if (ast_strlen_zero(data)) {
01641 ast_log(LOG_ERROR, "Minivm needs at least an account argument \n");
01642 return -1;
01643 }
01644 tmpptr = ast_strdupa((char *)data);
01645 if (!tmpptr) {
01646 ast_log(LOG_ERROR, "Out of memory\n");
01647 return -1;
01648 }
01649 argc = ast_app_separate_args(tmpptr, ',', argv, ARRAY_LEN(argv));
01650
01651 if (argc == 2 && !ast_strlen_zero(argv[1]))
01652 template = argv[1];
01653
01654 ast_copy_string(tmp, argv[0], sizeof(tmp));
01655 username = tmp;
01656 domain = strchr(tmp, '@');
01657 if (domain) {
01658 *domain = '\0';
01659 domain++;
01660 }
01661 if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
01662 ast_log(LOG_ERROR, "Need username@domain as argument. Sorry. Argument 0 %s\n", argv[0]);
01663 return -1;
01664 }
01665
01666 if(!(vmu = find_account(domain, username, TRUE))) {
01667
01668 ast_log(LOG_WARNING, "Could not allocate temporary memory for '%s@%s'\n", username, domain);
01669 pbx_builtin_setvar_helper(chan, "MVM_NOTIFY_STATUS", "FAILED");
01670 return -1;
01671 }
01672
01673 ast_channel_lock(chan);
01674 if ((filename = pbx_builtin_getvar_helper(chan, "MVM_FILENAME"))) {
01675 filename = ast_strdupa(filename);
01676 }
01677 ast_channel_unlock(chan);
01678
01679 if (!ast_strlen_zero(filename)) {
01680 ast_channel_lock(chan);
01681 if ((format = pbx_builtin_getvar_helper(chan, "MVM_FORMAT"))) {
01682 format = ast_strdupa(format);
01683 }
01684 if ((duration_string = pbx_builtin_getvar_helper(chan, "MVM_DURATION"))) {
01685 duration_string = ast_strdupa(duration_string);
01686 }
01687 ast_channel_unlock(chan);
01688 res = notify_new_message(chan, template, vmu, filename, atoi(duration_string), format, chan->cid.cid_num, chan->cid.cid_name);
01689 }
01690
01691 pbx_builtin_setvar_helper(chan, "MVM_NOTIFY_STATUS", res == 0 ? "SUCCESS" : "FAILED");
01692
01693
01694 if(ast_test_flag(vmu, MVM_ALLOCED))
01695 free_user(vmu);
01696
01697
01698
01699 return res;
01700
01701 }
01702
01703
01704 static int minivm_record_exec(struct ast_channel *chan, void *data)
01705 {
01706 int res = 0;
01707 char *tmp;
01708 struct leave_vm_options leave_options;
01709 int argc;
01710 char *argv[2];
01711 struct ast_flags flags = { 0 };
01712 char *opts[OPT_ARG_ARRAY_SIZE];
01713
01714 memset(&leave_options, 0, sizeof(leave_options));
01715
01716
01717 if (chan->_state != AST_STATE_UP)
01718 ast_answer(chan);
01719
01720 if (ast_strlen_zero(data)) {
01721 ast_log(LOG_ERROR, "Minivm needs at least an account argument \n");
01722 return -1;
01723 }
01724 tmp = ast_strdupa((char *)data);
01725 if (!tmp) {
01726 ast_log(LOG_ERROR, "Out of memory\n");
01727 return -1;
01728 }
01729 argc = ast_app_separate_args(tmp, ',', argv, ARRAY_LEN(argv));
01730 if (argc == 2) {
01731 if (ast_app_parse_options(minivm_app_options, &flags, opts, argv[1])) {
01732 return -1;
01733 }
01734 ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING );
01735 if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
01736 int gain;
01737
01738 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
01739 ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
01740 return -1;
01741 } else
01742 leave_options.record_gain = (signed char) gain;
01743 }
01744 }
01745
01746
01747 res = leave_voicemail(chan, argv[0], &leave_options);
01748
01749 if (res == ERROR_LOCK_PATH) {
01750 ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
01751 pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "FAILED");
01752 res = 0;
01753 }
01754 pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "SUCCESS");
01755
01756 return res;
01757 }
01758
01759
01760 static int minivm_greet_exec(struct ast_channel *chan, void *data)
01761 {
01762 struct leave_vm_options leave_options = { 0, '\0'};
01763 int argc;
01764 char *argv[2];
01765 struct ast_flags flags = { 0 };
01766 char *opts[OPT_ARG_ARRAY_SIZE];
01767 int res = 0;
01768 int ausemacro = 0;
01769 int ousemacro = 0;
01770 int ouseexten = 0;
01771 char tmp[PATH_MAX];
01772 char dest[PATH_MAX];
01773 char prefile[PATH_MAX] = "";
01774 char tempfile[PATH_MAX] = "";
01775 char ext_context[256] = "";
01776 char *domain;
01777 char ecodes[16] = "#";
01778 char *tmpptr;
01779 struct minivm_account *vmu;
01780 char *username = argv[0];
01781
01782 if (ast_strlen_zero(data)) {
01783 ast_log(LOG_ERROR, "Minivm needs at least an account argument \n");
01784 return -1;
01785 }
01786 tmpptr = ast_strdupa((char *)data);
01787 if (!tmpptr) {
01788 ast_log(LOG_ERROR, "Out of memory\n");
01789 return -1;
01790 }
01791 argc = ast_app_separate_args(tmpptr, ',', argv, ARRAY_LEN(argv));
01792
01793 if (argc == 2) {
01794 if (ast_app_parse_options(minivm_app_options, &flags, opts, argv[1]))
01795 return -1;
01796 ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING );
01797 }
01798
01799 ast_copy_string(tmp, argv[0], sizeof(tmp));
01800 username = tmp;
01801 domain = strchr(tmp, '@');
01802 if (domain) {
01803 *domain = '\0';
01804 domain++;
01805 }
01806 if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
01807 ast_log(LOG_ERROR, "Need username@domain as argument. Sorry. Argument: %s\n", argv[0]);
01808 return -1;
01809 }
01810 ast_debug(1, "Trying to find configuration for user %s in domain %s\n", username, domain);
01811
01812 if (!(vmu = find_account(domain, username, TRUE))) {
01813 ast_log(LOG_ERROR, "Could not allocate memory. \n");
01814 return -1;
01815 }
01816
01817
01818 if (chan->_state != AST_STATE_UP)
01819 ast_answer(chan);
01820
01821
01822 if (strcmp(vmu->domain, "localhost"))
01823 snprintf(ext_context, sizeof(ext_context), "%s@%s", username, vmu->domain);
01824 else
01825 ast_copy_string(ext_context, vmu->domain, sizeof(ext_context));
01826
01827 if (ast_test_flag(&leave_options, OPT_BUSY_GREETING)) {
01828 res = check_dirpath(dest, sizeof(dest), vmu->domain, username, "busy");
01829 if (res)
01830 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", MVM_SPOOL_DIR, vmu->domain, username);
01831 } else if (ast_test_flag(&leave_options, OPT_UNAVAIL_GREETING)) {
01832 res = check_dirpath(dest, sizeof(dest), vmu->domain, username, "unavail");
01833 if (res)
01834 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", MVM_SPOOL_DIR, vmu->domain, username);
01835 }
01836
01837 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", MVM_SPOOL_DIR, vmu->domain, username);
01838 if (!(res = check_dirpath(dest, sizeof(dest), vmu->domain, username, "temp"))) {
01839 ast_debug(2, "Temporary message directory does not exist, using default (%s)\n", tempfile);
01840 ast_copy_string(prefile, tempfile, sizeof(prefile));
01841 }
01842 ast_debug(2, "Preparing to play message ...\n");
01843
01844
01845 if (ast_test_flag(vmu, MVM_OPERATOR)) {
01846 if (!ast_strlen_zero(vmu->exit)) {
01847 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
01848 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
01849 ouseexten = 1;
01850 }
01851 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
01852 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
01853 ouseexten = 1;
01854 }
01855 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
01856 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
01857 ousemacro = 1;
01858 }
01859 }
01860
01861 if (!ast_strlen_zero(vmu->exit)) {
01862 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
01863 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
01864 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
01865 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
01866 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
01867 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
01868 ausemacro = 1;
01869 }
01870
01871 res = 0;
01872
01873 if (!ast_strlen_zero(prefile)) {
01874 if (ast_streamfile(chan, prefile, chan->language) > -1)
01875 res = ast_waitstream(chan, ecodes);
01876 } else {
01877 ast_debug(2, "%s doesn't exist, doing what we can\n", prefile);
01878 res = invent_message(chan, vmu->domain, username, ast_test_flag(&leave_options, OPT_BUSY_GREETING), ecodes);
01879 }
01880 if (res < 0) {
01881 ast_debug(2, "Hang up during prefile playback\n");
01882 pbx_builtin_setvar_helper(chan, "MVM_GREET_STATUS", "FAILED");
01883 if(ast_test_flag(vmu, MVM_ALLOCED))
01884 free_user(vmu);
01885 return -1;
01886 }
01887 if (res == '#') {
01888
01889 ast_set_flag(&leave_options, OPT_SILENT);
01890 res = 0;
01891 }
01892 if (!res && !ast_test_flag(&leave_options, OPT_SILENT)) {
01893 res = ast_streamfile(chan, SOUND_INTRO, chan->language);
01894 if (!res)
01895 res = ast_waitstream(chan, ecodes);
01896 if (res == '#') {
01897 ast_set_flag(&leave_options, OPT_SILENT);
01898 res = 0;
01899 }
01900 }
01901 if (res > 0)
01902 ast_stopstream(chan);
01903
01904
01905 if (res == '*') {
01906 chan->exten[0] = 'a';
01907 chan->exten[1] = '\0';
01908 if (!ast_strlen_zero(vmu->exit)) {
01909 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
01910 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
01911 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
01912 }
01913 chan->priority = 0;
01914 pbx_builtin_setvar_helper(chan, "MVM_GREET_STATUS", "USEREXIT");
01915 res = 0;
01916 } else if (res == '0') {
01917 if(ouseexten || ousemacro) {
01918 chan->exten[0] = 'o';
01919 chan->exten[1] = '\0';
01920 if (!ast_strlen_zero(vmu->exit)) {
01921 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
01922 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
01923 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
01924 }
01925 ast_play_and_wait(chan, "transfer");
01926 chan->priority = 0;
01927 pbx_builtin_setvar_helper(chan, "MVM_GREET_STATUS", "USEREXIT");
01928 }
01929 res = 0;
01930 } else if (res < 0) {
01931 pbx_builtin_setvar_helper(chan, "MVM_GREET_STATUS", "FAILED");
01932 res = -1;
01933 } else
01934 pbx_builtin_setvar_helper(chan, "MVM_GREET_STATUS", "SUCCESS");
01935
01936 if(ast_test_flag(vmu, MVM_ALLOCED))
01937 free_user(vmu);
01938
01939
01940
01941 return res;
01942
01943 }
01944
01945
01946 static int minivm_delete_exec(struct ast_channel *chan, void *data)
01947 {
01948 int res = 0;
01949 char filename[BUFSIZ];
01950
01951 if (!ast_strlen_zero(data)) {
01952 ast_copy_string(filename, (char *) data, sizeof(filename));
01953 } else {
01954 ast_channel_lock(chan);
01955 ast_copy_string(filename, pbx_builtin_getvar_helper(chan, "MVM_FILENAME"), sizeof(filename));
01956 ast_channel_unlock(chan);
01957 }
01958
01959 if (ast_strlen_zero(filename)) {
01960 ast_log(LOG_ERROR, "No filename given in application arguments or channel variable MVM_FILENAME\n");
01961 return res;
01962 }
01963
01964
01965
01966 if (ast_fileexists(filename, NULL, NULL) > 0) {
01967 res = vm_delete(filename);
01968 if (res) {
01969 ast_debug(2, "Can't delete file: %s\n", filename);
01970 pbx_builtin_setvar_helper(chan, "MVM_DELETE_STATUS", "FAILED");
01971 } else {
01972 ast_debug(2, "Deleted voicemail file :: %s \n", filename);
01973 pbx_builtin_setvar_helper(chan, "MVM_DELETE_STATUS", "SUCCESS");
01974 }
01975 } else {
01976 ast_debug(2, "Filename does not exist: %s\n", filename);
01977 pbx_builtin_setvar_helper(chan, "MVM_DELETE_STATUS", "FAILED");
01978 }
01979
01980 return res;
01981 }
01982
01983
01984 static int minivm_accmess_exec(struct ast_channel *chan, void *data)
01985 {
01986 int argc = 0;
01987 char *argv[2];
01988 char filename[PATH_MAX];
01989 char tmp[PATH_MAX];
01990 char *domain;
01991 char *tmpptr = NULL;
01992 struct minivm_account *vmu;
01993 char *username = argv[0];
01994 struct ast_flags flags = { 0 };
01995 char *opts[OPT_ARG_ARRAY_SIZE];
01996 int error = FALSE;
01997 char *message = NULL;
01998 char *prompt = NULL;
01999 int duration;
02000 int cmd;
02001
02002 if (ast_strlen_zero(data)) {
02003 ast_log(LOG_ERROR, "MinivmAccmess needs at least two arguments: account and option\n");
02004 error = TRUE;
02005 } else
02006 tmpptr = ast_strdupa((char *)data);
02007 if (!error) {
02008 if (!tmpptr) {
02009 ast_log(LOG_ERROR, "Out of memory\n");
02010 error = TRUE;
02011 } else
02012 argc = ast_app_separate_args(tmpptr, ',', argv, ARRAY_LEN(argv));
02013 }
02014
02015 if (argc <=1) {
02016 ast_log(LOG_ERROR, "MinivmAccmess needs at least two arguments: account and option\n");
02017 error = TRUE;
02018 }
02019 if (!error && strlen(argv[1]) > 1) {
02020 ast_log(LOG_ERROR, "MinivmAccmess can only handle one option at a time. Bad option string: %s\n", argv[1]);
02021 error = TRUE;
02022 }
02023
02024 if (!error && ast_app_parse_options(minivm_accmess_options, &flags, opts, argv[1])) {
02025 ast_log(LOG_ERROR, "Can't parse option %s\n", argv[1]);
02026 error = TRUE;
02027 }
02028
02029 if (error) {
02030 pbx_builtin_setvar_helper(chan, "MVM_ACCMESS_STATUS", "FAILED");
02031 return -1;
02032 }
02033
02034 ast_copy_string(tmp, argv[0], sizeof(tmp));
02035 username = tmp;
02036 domain = strchr(tmp, '@');
02037 if (domain) {
02038 *domain = '\0';
02039 domain++;
02040 }
02041 if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
02042 ast_log(LOG_ERROR, "Need username@domain as argument. Sorry. Argument 0 %s\n", argv[0]);
02043 pbx_builtin_setvar_helper(chan, "MVM_ACCMESS_STATUS", "FAILED");
02044 return -1;
02045 }
02046
02047 if(!(vmu = find_account(domain, username, TRUE))) {
02048
02049 ast_log(LOG_WARNING, "Could not allocate temporary memory for '%s@%s'\n", username, domain);
02050 pbx_builtin_setvar_helper(chan, "MVM_ACCMESS_STATUS", "FAILED");
02051 return -1;
02052 }
02053
02054
02055 if (chan->_state != AST_STATE_UP)
02056 ast_answer(chan);
02057
02058
02059 if (ast_test_flag(&flags, OPT_BUSY_GREETING)) {
02060 message = "busy";
02061 prompt = "vm-rec-busy";
02062 } else if (ast_test_flag(&flags, OPT_UNAVAIL_GREETING)) {
02063 message = "unavailable";
02064 prompt = "vm-rec-unv";
02065 } else if (ast_test_flag(&flags, OPT_TEMP_GREETING)) {
02066 message = "temp";
02067 prompt = "vm-rec-temp";
02068 } else if (ast_test_flag(&flags, OPT_NAME_GREETING)) {
02069 message = "greet";
02070 prompt = "vm-rec-name";
02071 }
02072 snprintf(filename,sizeof(filename), "%s%s/%s/%s", MVM_SPOOL_DIR, vmu->domain, vmu->username, message);
02073
02074 cmd = play_record_review(chan, prompt, filename, global_maxgreet, default_vmformat, 0, vmu, &duration, NULL, FALSE);
02075
02076 ast_debug(1, "Recorded new %s message in %s (duration %d)\n", message, filename, duration);
02077
02078 if(ast_test_flag(vmu, MVM_ALLOCED))
02079 free_user(vmu);
02080
02081 pbx_builtin_setvar_helper(chan, "MVM_ACCMESS_STATUS", "SUCCESS");
02082
02083
02084 return 0;
02085 }
02086
02087
02088 static int create_vmaccount(char *name, struct ast_variable *var, int realtime)
02089 {
02090 struct minivm_account *vmu;
02091 char *domain;
02092 char *username;
02093 char accbuf[BUFSIZ];
02094
02095 ast_debug(3, "Creating %s account for [%s]\n", realtime ? "realtime" : "static", name);
02096
02097 ast_copy_string(accbuf, name, sizeof(accbuf));
02098 username = accbuf;
02099 domain = strchr(accbuf, '@');
02100 if (domain) {
02101 *domain = '\0';
02102 domain++;
02103 }
02104 if (ast_strlen_zero(domain)) {
02105 ast_log(LOG_ERROR, "No domain given for mini-voicemail account %s. Not configured.\n", name);
02106 return 0;
02107 }
02108
02109 ast_debug(3, "Creating static account for user %s domain %s\n", username, domain);
02110
02111
02112 vmu = ast_calloc(1, sizeof(*vmu));
02113 if (!vmu)
02114 return 0;
02115
02116 ast_copy_string(vmu->domain, domain, sizeof(vmu->domain));
02117 ast_copy_string(vmu->username, username, sizeof(vmu->username));
02118
02119 populate_defaults(vmu);
02120
02121 ast_debug(3, "...Configuring account %s\n", name);
02122
02123 while (var) {
02124 ast_debug(3, "Configuring %s = \"%s\" for account %s\n", var->name, var->value, name);
02125 if (!strcasecmp(var->name, "serveremail")) {
02126 ast_copy_string(vmu->serveremail, var->value, sizeof(vmu->serveremail));
02127 } else if (!strcasecmp(var->name, "email")) {
02128 ast_copy_string(vmu->email, var->value, sizeof(vmu->email));
02129 } else if (!strcasecmp(var->name, "accountcode")) {
02130 ast_copy_string(vmu->accountcode, var->value, sizeof(vmu->accountcode));
02131 } else if (!strcasecmp(var->name, "pincode")) {
02132 ast_copy_string(vmu->pincode, var->value, sizeof(vmu->pincode));
02133 } else if (!strcasecmp(var->name, "domain")) {
02134 ast_copy_string(vmu->domain, var->value, sizeof(vmu->domain));
02135 } else if (!strcasecmp(var->name, "language")) {
02136 ast_copy_string(vmu->language, var->value, sizeof(vmu->language));
02137 } else if (!strcasecmp(var->name, "timezone")) {
02138 ast_copy_string(vmu->zonetag, var->value, sizeof(vmu->zonetag));
02139 } else if (!strcasecmp(var->name, "externnotify")) {
02140 ast_copy_string(vmu->externnotify, var->value, sizeof(vmu->externnotify));
02141 } else if (!strcasecmp(var->name, "etemplate")) {
02142 ast_copy_string(vmu->etemplate, var->value, sizeof(vmu->etemplate));
02143 } else if (!strcasecmp(var->name, "ptemplate")) {
02144 ast_copy_string(vmu->ptemplate, var->value, sizeof(vmu->ptemplate));
02145 } else if (!strcasecmp(var->name, "fullname")) {
02146 ast_copy_string(vmu->fullname, var->value, sizeof(vmu->fullname));
02147 } else if (!strcasecmp(var->name, "setvar")) {
02148 char *varval;
02149 char *varname = ast_strdupa(var->value);
02150 struct ast_variable *tmpvar;
02151
02152 if (varname && (varval = strchr(varname, '='))) {
02153 *varval = '\0';
02154 varval++;
02155 if ((tmpvar = ast_variable_new(varname, varval, ""))) {
02156 tmpvar->next = vmu->chanvars;
02157 vmu->chanvars = tmpvar;
02158 }
02159 }
02160 } else if (!strcasecmp(var->name, "pager")) {
02161 ast_copy_string(vmu->pager, var->value, sizeof(vmu->pager));
02162 } else if (!strcasecmp(var->name, "volgain")) {
02163 sscanf(var->value, "%lf", &vmu->volgain);
02164 } else {
02165 ast_log(LOG_ERROR, "Unknown configuration option for minivm account %s : %s\n", name, var->name);
02166 }
02167 var = var->next;
02168 }
02169 ast_debug(3, "...Linking account %s\n", name);
02170
02171 AST_LIST_LOCK(&minivm_accounts);
02172 AST_LIST_INSERT_TAIL(&minivm_accounts, vmu, list);
02173 AST_LIST_UNLOCK(&minivm_accounts);
02174
02175 global_stats.voicemailaccounts++;
02176
02177 ast_debug(2, "MINIVM :: Created account %s@%s - tz %s etemplate %s %s\n", username, domain, ast_strlen_zero(vmu->zonetag) ? "" : vmu->zonetag, ast_strlen_zero(vmu->etemplate) ? "" : vmu->etemplate, realtime ? "(realtime)" : "");
02178 return 0;
02179 }
02180
02181
02182 static void free_zone(struct minivm_zone *z)
02183 {
02184 ast_free(z);
02185 }
02186
02187
02188 static void timezone_destroy_list(void)
02189 {
02190 struct minivm_zone *this;
02191
02192 AST_LIST_LOCK(&minivm_zones);
02193 while ((this = AST_LIST_REMOVE_HEAD(&minivm_zones, list)))
02194 free_zone(this);
02195
02196 AST_LIST_UNLOCK(&minivm_zones);
02197 }
02198
02199
02200 static int timezone_add(const char *zonename, const char *config)
02201 {
02202 struct minivm_zone *newzone;
02203 char *msg_format, *timezone_str;
02204
02205 newzone = ast_calloc(1, sizeof(*newzone));
02206 if (newzone == NULL)
02207 return 0;
02208
02209 msg_format = ast_strdupa(config);
02210 if (msg_format == NULL) {
02211 ast_log(LOG_WARNING, "Out of memory.\n");
02212 ast_free(newzone);
02213 return 0;
02214 }
02215
02216 timezone_str = strsep(&msg_format, "|");
02217 if (!msg_format) {
02218 ast_log(LOG_WARNING, "Invalid timezone definition : %s\n", zonename);
02219 ast_free(newzone);
02220 return 0;
02221 }
02222
02223 ast_copy_string(newzone->name, zonename, sizeof(newzone->name));
02224 ast_copy_string(newzone->timezone, timezone_str, sizeof(newzone->timezone));
02225 ast_copy_string(newzone->msg_format, msg_format, sizeof(newzone->msg_format));
02226
02227 AST_LIST_LOCK(&minivm_zones);
02228 AST_LIST_INSERT_TAIL(&minivm_zones, newzone, list);
02229 AST_LIST_UNLOCK(&minivm_zones);
02230
02231 global_stats.timezones++;
02232
02233 return 0;
02234 }
02235
02236
02237 static char *message_template_parse_filebody(const char *filename) {
02238 char buf[BUFSIZ * 6];
02239 char readbuf[BUFSIZ];
02240 char filenamebuf[BUFSIZ];
02241 char *writepos;
02242 char *messagebody;
02243 FILE *fi;
02244 int lines = 0;
02245
02246 if (ast_strlen_zero(filename))
02247 return NULL;
02248 if (*filename == '/')
02249 ast_copy_string(filenamebuf, filename, sizeof(filenamebuf));
02250 else
02251 snprintf(filenamebuf, sizeof(filenamebuf), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
02252
02253 if (!(fi = fopen(filenamebuf, "r"))) {
02254 ast_log(LOG_ERROR, "Can't read message template from file: %s\n", filenamebuf);
02255 return NULL;
02256 }
02257 writepos = buf;
02258 while (fgets(readbuf, sizeof(readbuf), fi)) {
02259 lines ++;
02260 if (writepos != buf) {
02261 *writepos = '\n';
02262 writepos++;
02263 }
02264 ast_copy_string(writepos, readbuf, sizeof(buf) - (writepos - buf));
02265 writepos += strlen(readbuf) - 1;
02266 }
02267 fclose(fi);
02268 messagebody = ast_calloc(1, strlen(buf + 1));
02269 ast_copy_string(messagebody, buf, strlen(buf) + 1);
02270 ast_debug(4, "---> Size of allocation %d\n", (int) strlen(buf + 1) );
02271 ast_debug(4, "---> Done reading message template : \n%s\n---- END message template--- \n", messagebody);
02272
02273 return messagebody;
02274 }
02275
02276
02277 static char *message_template_parse_emailbody(const char *configuration)
02278 {
02279 char *tmpread, *tmpwrite;
02280 char *emailbody = ast_strdup(configuration);
02281
02282
02283 tmpread = tmpwrite = emailbody;
02284 while ((tmpwrite = strchr(tmpread,'\\'))) {
02285 int len = strlen("\n");
02286 switch (tmpwrite[1]) {
02287 case 'n':
02288 memmove(tmpwrite + len, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
02289 strncpy(tmpwrite, "\n", len);
02290 break;
02291 case 't':
02292 memmove(tmpwrite + len, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
02293 strncpy(tmpwrite, "\t", len);
02294 break;
02295 default:
02296 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
02297 }
02298 tmpread = tmpwrite + len;
02299 }
02300 return emailbody;
02301 }
02302
02303
02304 static int apply_general_options(struct ast_variable *var)
02305 {
02306 int error = 0;
02307
02308 while (var) {
02309
02310 if (!strcmp(var->name, "mailcmd")) {
02311 ast_copy_string(global_mailcmd, var->value, sizeof(global_mailcmd));
02312 } else if (!strcmp(var->name, "maxgreet")) {
02313 global_maxgreet = atoi(var->value);
02314 } else if (!strcmp(var->name, "maxsilence")) {
02315 global_maxsilence = atoi(var->value);
02316 if (global_maxsilence > 0)
02317 global_maxsilence *= 1000;
02318 } else if (!strcmp(var->name, "logfile")) {
02319 if (!ast_strlen_zero(var->value) ) {
02320 if(*(var->value) == '/')
02321 ast_copy_string(global_logfile, var->value, sizeof(global_logfile));
02322 else
02323 snprintf(global_logfile, sizeof(global_logfile), "%s/%s", ast_config_AST_LOG_DIR, var->value);
02324 }
02325 } else if (!strcmp(var->name, "externnotify")) {
02326
02327 ast_copy_string(global_externnotify, var->value, sizeof(global_externnotify));
02328 } else if (!strcmp(var->name, "silencetreshold")) {
02329
02330 global_silencethreshold = atoi(var->value);
02331 } else if (!strcmp(var->name, "maxmessage")) {
02332 int x;
02333 if (sscanf(var->value, "%d", &x) == 1) {
02334 global_vmmaxmessage = x;
02335 } else {
02336 error ++;
02337 ast_log(LOG_WARNING, "Invalid max message time length\n");
02338 }
02339 } else if (!strcmp(var->name, "minmessage")) {
02340 int x;
02341 if (sscanf(var->value, "%d", &x) == 1) {
02342 global_vmminmessage = x;
02343 if (global_maxsilence <= global_vmminmessage)
02344 ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
02345 } else {
02346 error ++;
02347 ast_log(LOG_WARNING, "Invalid min message time length\n");
02348 }
02349 } else if (!strcmp(var->name, "format")) {
02350 ast_copy_string(default_vmformat, var->value, sizeof(default_vmformat));
02351 } else if (!strcmp(var->name, "review")) {
02352 ast_set2_flag((&globalflags), ast_true(var->value), MVM_REVIEW);
02353 } else if (!strcmp(var->name, "operator")) {
02354 ast_set2_flag((&globalflags), ast_true(var->value), MVM_OPERATOR);
02355 }
02356 var = var->next;
02357 }
02358 return error;
02359 }
02360
02361
02362 static int load_config(int reload)
02363 {
02364 struct ast_config *cfg;
02365 struct ast_variable *var;
02366 char *cat;
02367 const char *chanvar;
02368 int error = 0;
02369 struct minivm_template *template;
02370 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
02371
02372 cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
02373 if (cfg == CONFIG_STATUS_FILEUNCHANGED)
02374 return 0;
02375
02376 ast_mutex_lock(&minivmlock);
02377
02378
02379 message_destroy_list();
02380 timezone_destroy_list();
02381 vmaccounts_destroy_list();
02382 ast_debug(2, "Destroyed memory objects...\n");
02383
02384
02385 global_externnotify[0] = '\0';
02386 global_logfile[0] = '\0';
02387 global_vmmaxmessage = 2000;
02388 global_maxgreet = 2000;
02389 global_vmminmessage = 0;
02390 strcpy(global_mailcmd, SENDMAIL);
02391 global_maxsilence = 0;
02392 global_saydurationminfo = 2;
02393 ast_copy_string(default_vmformat, "wav", sizeof(default_vmformat));
02394 ast_set2_flag((&globalflags), FALSE, MVM_REVIEW);
02395 ast_set2_flag((&globalflags), FALSE, MVM_OPERATOR);
02396 strcpy(global_charset, "ISO-8859-1");
02397
02398 memset(&global_stats, 0, sizeof(global_stats));
02399 global_stats.reset = ast_tvnow();
02400
02401 global_silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
02402
02403
02404 if (!cfg) {
02405 ast_log(LOG_WARNING, "Failed to load configuration file. Module activated with default settings.\n");
02406 ast_mutex_unlock(&minivmlock);
02407 return 0;
02408 }
02409
02410 ast_debug(2, "Loaded configuration file, now parsing\n");
02411
02412
02413
02414 cat = ast_category_browse(cfg, NULL);
02415 while (cat) {
02416 ast_debug(3, "Found configuration section [%s]\n", cat);
02417 if (!strcasecmp(cat, "general")) {
02418
02419 error += apply_general_options(ast_variable_browse(cfg, cat));
02420 } else if (!strncasecmp(cat, "template-", 9)) {
02421
02422 char *name = cat + 9;
02423
02424
02425 error += message_template_build(name, ast_variable_browse(cfg, cat));
02426 } else {
02427 var = ast_variable_browse(cfg, cat);
02428 if (!strcasecmp(cat, "zonemessages")) {
02429
02430 while (var) {
02431 timezone_add(var->name, var->value);
02432 var = var->next;
02433 }
02434 } else {
02435
02436 error += create_vmaccount(cat, var, FALSE);
02437 }
02438 }
02439
02440 cat = ast_category_browse(cfg, cat);
02441 }
02442
02443
02444 message_template_build("email-default", NULL);
02445 template = message_template_find("email-default");
02446
02447
02448 if ((chanvar = ast_variable_retrieve(cfg, "general", "emaildateformat")))
02449 ast_copy_string(template->dateformat, chanvar, sizeof(template->dateformat));
02450 if ((chanvar = ast_variable_retrieve(cfg, "general", "emailfromstring")))
02451 ast_copy_string(template->fromaddress, chanvar, sizeof(template->fromaddress));
02452 if ((chanvar = ast_variable_retrieve(cfg, "general", "emailaaddress")))
02453 ast_copy_string(template->serveremail, chanvar, sizeof(template->serveremail));
02454 if ((chanvar = ast_variable_retrieve(cfg, "general", "emailcharset")))
02455 ast_copy_string(template->charset, chanvar, sizeof(template->charset));
02456 if ((chanvar = ast_variable_retrieve(cfg, "general", "emailsubject")))
02457 ast_copy_string(template->subject, chanvar, sizeof(template->subject));
02458 if ((chanvar = ast_variable_retrieve(cfg, "general", "emailbody")))
02459 template->body = message_template_parse_emailbody(chanvar);
02460 template->attachment = TRUE;
02461
02462 message_template_build("pager-default", NULL);
02463 template = message_template_find("pager-default");
02464 if ((chanvar = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
02465 ast_copy_string(template->fromaddress, chanvar, sizeof(template->fromaddress));
02466 if ((chanvar = ast_variable_retrieve(cfg, "general", "pageraddress")))
02467 ast_copy_string(template->serveremail, chanvar, sizeof(template->serveremail));
02468 if ((chanvar = ast_variable_retrieve(cfg, "general", "pagercharset")))
02469 ast_copy_string(template->charset, chanvar, sizeof(template->charset));
02470 if ((chanvar = ast_variable_retrieve(cfg, "general", "pagersubject")))
02471 ast_copy_string(template->subject, chanvar,sizeof(template->subject));
02472 if ((chanvar = ast_variable_retrieve(cfg, "general", "pagerbody")))
02473 template->body = message_template_parse_emailbody(chanvar);
02474 template->attachment = FALSE;
02475
02476 if (error)
02477 ast_log(LOG_ERROR, "--- A total of %d errors found in mini-voicemail configuration\n", error);
02478
02479 ast_mutex_unlock(&minivmlock);
02480 ast_config_destroy(cfg);
02481
02482
02483 if(minivmlogfile)
02484 fclose(minivmlogfile);
02485
02486
02487 if(!ast_strlen_zero(global_logfile)) {
02488 minivmlogfile = fopen(global_logfile, "a");
02489 if(!minivmlogfile)
02490 ast_log(LOG_ERROR, "Failed to open minivm log file %s : %s\n", global_logfile, strerror(errno));
02491 if (minivmlogfile)
02492 ast_debug(3, "Opened log file %s \n", global_logfile);
02493 }
02494
02495 return 0;
02496 }
02497
02498
02499 static char *handle_minivm_list_templates(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02500 {
02501 struct minivm_template *this;
02502 #define HVLT_OUTPUT_FORMAT "%-15s %-10s %-10s %-15.15s %-50s\n"
02503 int count = 0;
02504
02505 switch (cmd) {
02506 case CLI_INIT:
02507 e->command = "minivm list templates";
02508 e->usage =
02509 "Usage: minivm list templates\n"
02510 " Lists message templates for e-mail, paging and IM\n";
02511 return NULL;
02512 case CLI_GENERATE:
02513 return NULL;
02514 }
02515
02516 if (a->argc > 3)
02517 return CLI_SHOWUSAGE;
02518
02519 AST_LIST_LOCK(&message_templates);
02520 if (AST_LIST_EMPTY(&message_templates)) {
02521 ast_cli(a->fd, "There are no message templates defined\n");
02522 AST_LIST_UNLOCK(&message_templates);
02523 return CLI_FAILURE;
02524 }
02525 ast_cli(a->fd, HVLT_OUTPUT_FORMAT, "Template name", "Charset", "Locale", "Attach media", "Subject");
02526 ast_cli(a->fd, HVLT_OUTPUT_FORMAT, "-------------", "-------", "------", "------------", "-------");
02527 AST_LIST_TRAVERSE(&message_templates, this, list) {
02528 ast_cli(a->fd, HVLT_OUTPUT_FORMAT, this->name,
02529 this->charset ? this->charset : "-",
02530 this->locale ? this->locale : "-",
02531 this->attachment ? "Yes" : "No",
02532 this->subject ? this->subject : "-");
02533 count++;
02534 }
02535 AST_LIST_UNLOCK(&message_templates);
02536 ast_cli(a->fd, "\n * Total: %d minivoicemail message templates\n", count);
02537 return CLI_SUCCESS;
02538 }
02539
02540 static char *complete_minivm_show_users(const char *line, const char *word, int pos, int state)
02541 {
02542 int which = 0;
02543 int wordlen;
02544 struct minivm_account *vmu;
02545 const char *domain = "";
02546
02547
02548 if (pos > 4)
02549 return NULL;
02550 if (pos == 3)
02551 return (state == 0) ? ast_strdup("for") : NULL;
02552 wordlen = strlen(word);
02553 AST_LIST_TRAVERSE(&minivm_accounts, vmu, list) {
02554 if (!strncasecmp(word, vmu->domain, wordlen)) {
02555 if (domain && strcmp(domain, vmu->domain) && ++which > state)
02556 return ast_strdup(vmu->domain);
02557
02558 domain = vmu->domain;
02559 }
02560 }
02561 return NULL;
02562 }
02563
02564
02565 static char *handle_minivm_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02566 {
02567 struct minivm_account *vmu;
02568 #define HMSU_OUTPUT_FORMAT "%-23s %-15s %-15s %-10s %-10s %-50s\n"
02569 int count = 0;
02570
02571 switch (cmd) {
02572 case CLI_INIT:
02573 e->command = "minivm list accounts";
02574 e->usage =
02575 "Usage: minivm list accounts\n"
02576 " Lists all mailboxes currently set up\n";
02577 return NULL;
02578 case CLI_GENERATE:
02579 return complete_minivm_show_users(a->line, a->word, a->pos, a->n);
02580 }
02581
02582 if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
02583 return CLI_SHOWUSAGE;
02584 if ((a->argc == 5) && strcmp(a->argv[3],"for"))
02585 return CLI_SHOWUSAGE;
02586
02587 AST_LIST_LOCK(&minivm_accounts);
02588 if (AST_LIST_EMPTY(&minivm_accounts)) {
02589 ast_cli(a->fd, "There are no voicemail users currently defined\n");
02590 AST_LIST_UNLOCK(&minivm_accounts);
02591 return CLI_FAILURE;
02592 }
02593 ast_cli(a->fd, HMSU_OUTPUT_FORMAT, "User", "E-Template", "P-template", "Zone", "Format", "Full name");
02594 ast_cli(a->fd, HMSU_OUTPUT_FORMAT, "----", "----------", "----------", "----", "------", "---------");
02595 AST_LIST_TRAVERSE(&minivm_accounts, vmu, list) {
02596 char tmp[256] = "";
02597 if ((a->argc == 3) || ((a->argc == 5) && !strcmp(a->argv[4], vmu->domain))) {
02598 count++;
02599 snprintf(tmp, sizeof(tmp), "%s@%s", vmu->username, vmu->domain);
02600 ast_cli(a->fd, HMSU_OUTPUT_FORMAT, tmp, vmu->etemplate ? vmu->etemplate : "-",
02601 vmu->ptemplate ? vmu->ptemplate : "-",
02602 vmu->zonetag ? vmu->zonetag : "-",
02603 vmu->attachfmt ? vmu->attachfmt : "-",
02604 vmu->fullname);
02605 }
02606 }
02607 AST_LIST_UNLOCK(&minivm_accounts);
02608 ast_cli(a->fd, "\n * Total: %d minivoicemail accounts\n", count);
02609 return CLI_SUCCESS;
02610 }
02611
02612
02613 static char *handle_minivm_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02614 {
02615 struct minivm_zone *zone;
02616 #define HMSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
02617 char *res = CLI_SUCCESS;
02618
02619 switch (cmd) {
02620 case CLI_INIT:
02621 e->command = "minivm list zones";
02622 e->usage =
02623 "Usage: minivm list zones\n"
02624 " Lists zone message formats\n";
02625 return NULL;
02626 case CLI_GENERATE:
02627 return NULL;
02628 }
02629
02630 if (a->argc != e->args)
02631 return CLI_SHOWUSAGE;
02632
02633 AST_LIST_LOCK(&minivm_zones);
02634 if (!AST_LIST_EMPTY(&minivm_zones)) {
02635 ast_cli(a->fd, HMSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
02636 ast_cli(a->fd, HMSZ_OUTPUT_FORMAT, "----", "--------", "--------------");
02637 AST_LIST_TRAVERSE(&minivm_zones, zone, list) {
02638 ast_cli(a->fd, HMSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
02639 }
02640 } else {
02641 ast_cli(a->fd, "There are no voicemail zones currently defined\n");
02642 res = CLI_FAILURE;
02643 }
02644 AST_LIST_UNLOCK(&minivm_zones);
02645
02646 return res;
02647 }
02648
02649
02650 static char *handle_minivm_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02651 {
02652 switch (cmd) {
02653 case CLI_INIT:
02654 e->command = "minivm show settings";
02655 e->usage =
02656 "Usage: minivm show settings\n"
02657 " Display Mini-Voicemail general settings\n";
02658 return NULL;
02659 case CLI_GENERATE:
02660 return NULL;
02661 }
02662
02663 ast_cli(a->fd, "* Mini-Voicemail general settings\n");
02664 ast_cli(a->fd, " -------------------------------\n");
02665 ast_cli(a->fd, "\n");
02666 ast_cli(a->fd, " Mail command (shell): %s\n", global_mailcmd);
02667 ast_cli(a->fd, " Max silence: %d\n", global_maxsilence);
02668 ast_cli(a->fd, " Silence threshold: %d\n", global_silencethreshold);
02669 ast_cli(a->fd, " Max message length (secs): %d\n", global_vmmaxmessage);
02670 ast_cli(a->fd, " Min message length (secs): %d\n", global_vmminmessage);
02671 ast_cli(a->fd, " Default format: %s\n", default_vmformat);
02672 ast_cli(a->fd, " Extern notify (shell): %s\n", global_externnotify);
02673 ast_cli(a->fd, " Logfile: %s\n", global_logfile[0] ? global_logfile : "<disabled>");
02674 ast_cli(a->fd, " Operator exit: %s\n", ast_test_flag(&globalflags, MVM_OPERATOR) ? "Yes" : "No");
02675 ast_cli(a->fd, " Message review: %s\n", ast_test_flag(&globalflags, MVM_REVIEW) ? "Yes" : "No");
02676
02677 ast_cli(a->fd, "\n");
02678 return CLI_SUCCESS;
02679 }
02680
02681
02682 static char *handle_minivm_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02683 {
02684 struct ast_tm timebuf;
02685 char buf[BUFSIZ];
02686
02687 switch (cmd) {
02688
02689 case CLI_INIT:
02690 e->command = "minivm show stats";
02691 e->usage =
02692 "Usage: minivm show stats\n"
02693 " Display Mini-Voicemail counters\n";
02694 return NULL;
02695 case CLI_GENERATE:
02696 return NULL;
02697 }
02698
02699 ast_cli(a->fd, "* Mini-Voicemail statistics\n");
02700 ast_cli(a->fd, " -------------------------\n");
02701 ast_cli(a->fd, "\n");
02702 ast_cli(a->fd, " Voicemail accounts: %5d\n", global_stats.voicemailaccounts);
02703 ast_cli(a->fd, " Templates: %5d\n", global_stats.templates);
02704 ast_cli(a->fd, " Timezones: %5d\n", global_stats.timezones);
02705 if (global_stats.receivedmessages == 0) {
02706 ast_cli(a->fd, " Received messages since last reset: <none>\n");
02707 } else {
02708 ast_cli(a->fd, " Received messages since last reset: %d\n", global_stats.receivedmessages);
02709 ast_localtime(&global_stats.lastreceived, &timebuf, NULL);
02710 ast_strftime(buf, sizeof(buf), "%a %b %e %r %Z %Y", &timebuf);
02711 ast_cli(a->fd, " Last received voicemail: %s\n", buf);
02712 }
02713 ast_localtime(&global_stats.reset, &timebuf, NULL);
02714 ast_strftime(buf, sizeof(buf), "%a %b %e %r %Z %Y", &timebuf);
02715 ast_cli(a->fd, " Last reset: %s\n", buf);
02716
02717 ast_cli(a->fd, "\n");
02718 return CLI_SUCCESS;
02719 }
02720
02721
02722 static int minivm_account_func_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
02723 {
02724 struct minivm_account *vmu;
02725 char *username, *domain, *colname;
02726
02727 if (!(username = ast_strdupa(data))) {
02728 ast_log(LOG_ERROR, "Memory Error!\n");
02729 return -1;
02730 }
02731
02732 if ((colname = strchr(username, ':'))) {
02733 *colname = '\0';
02734 colname++;
02735 } else {
02736 colname = "path";
02737 }
02738 if ((domain = strchr(username, '@'))) {
02739 *domain = '\0';
02740 domain++;
02741 }
02742 if (ast_strlen_zero(username) || ast_strlen_zero(domain)) {
02743 ast_log(LOG_ERROR, "This function needs a username and a domain: username@domain\n");
02744 return 0;
02745 }
02746
02747 if (!(vmu = find_account(domain, username, TRUE)))
02748 return 0;
02749
02750 if (!strcasecmp(colname, "hasaccount")) {
02751 ast_copy_string(buf, (ast_test_flag(vmu, MVM_ALLOCED) ? "0" : "1"), len);
02752 } else if (!strcasecmp(colname, "fullname")) {
02753 ast_copy_string(buf, vmu->fullname, len);
02754 } else if (!strcasecmp(colname, "email")) {
02755 if (!ast_strlen_zero(vmu->email))
02756 ast_copy_string(buf, vmu->email, len);
02757 else
02758 snprintf(buf, len, "%s@%s", vmu->username, vmu->domain);
02759 } else if (!strcasecmp(colname, "pager")) {
02760 ast_copy_string(buf, vmu->pager, len);
02761 } else if (!strcasecmp(colname, "etemplate")) {
02762 if (!ast_strlen_zero(vmu->etemplate))
02763 ast_copy_string(buf, vmu->etemplate, len);
02764 else
02765 ast_copy_string(buf, "email-default", len);
02766 } else if (!strcasecmp(colname, "language")) {
02767 ast_copy_string(buf, vmu->language, len);
02768 } else if (!strcasecmp(colname, "timezone")) {
02769 ast_copy_string(buf, vmu->zonetag, len);
02770 } else if (!strcasecmp(colname, "ptemplate")) {
02771 if (!ast_strlen_zero(vmu->ptemplate))
02772 ast_copy_string(buf, vmu->ptemplate, len);
02773 else
02774 ast_copy_string(buf, "email-default", len);
02775 } else if (!strcasecmp(colname, "accountcode")) {
02776 ast_copy_string(buf, vmu->accountcode, len);
02777 } else if (!strcasecmp(colname, "pincode")) {
02778 ast_copy_string(buf, vmu->pincode, len);
02779 } else if (!strcasecmp(colname, "path")) {
02780 check_dirpath(buf, len, vmu->domain, vmu->username, NULL);
02781 } else {
02782 struct ast_variable *var;
02783 int found = 0;
02784
02785 for (var = vmu->chanvars ; var ; var = var->next)
02786 if (!strcmp(var->name, colname)) {
02787 ast_copy_string(buf, var->value, len);
02788 found = 1;
02789 break;
02790 }
02791 }
02792
02793 if(ast_test_flag(vmu, MVM_ALLOCED))
02794 free_user(vmu);
02795
02796 return 0;
02797 }
02798
02799
02800
02801
02802
02803
02804 static int vm_lock_path(const char *path)
02805 {
02806 switch (ast_lock_path(path)) {
02807 case AST_LOCK_TIMEOUT:
02808 return -1;
02809 default:
02810 return 0;
02811 }
02812 }
02813
02814
02815
02816
02817
02818
02819
02820
02821 static int access_counter_file(char *directory, char *countername, int value, int operand)
02822 {
02823 char filename[BUFSIZ];
02824 char readbuf[BUFSIZ];
02825 FILE *counterfile;
02826 int old = 0, counter = 0;
02827
02828
02829 if (vm_lock_path(directory)) {
02830 return -1;
02831 }
02832 snprintf(filename, sizeof(filename), "%s/%s.counter", directory, countername);
02833 if (operand != 1) {
02834 counterfile = fopen(filename, "r");
02835 if (counterfile) {
02836 if(fgets(readbuf, sizeof(readbuf), counterfile)) {
02837 ast_debug(3, "Read this string from counter file: %s\n", readbuf);
02838 old = counter = atoi(readbuf);
02839 }
02840 fclose(counterfile);
02841 }
02842 }
02843 switch (operand) {
02844 case 0:
02845 ast_unlock_path(directory);
02846 ast_debug(2, "MINIVM Counter %s/%s: Value %d\n", directory, countername, counter);
02847 return counter;
02848 break;
02849 case 1:
02850 counter = value;
02851 break;
02852 case 2:
02853 counter += value;
02854 if (counter < 0)
02855 counter = 0;
02856 break;
02857 }
02858
02859
02860 counterfile = fopen(filename, "w");
02861 if (!counterfile) {
02862 ast_log(LOG_ERROR, "Could not open counter file for writing : %s - %s\n", filename, strerror(errno));
02863 ast_unlock_path(directory);
02864 return -1;
02865 }
02866 fprintf(counterfile, "%d\n\n", counter);
02867 fclose(counterfile);
02868 ast_unlock_path(directory);
02869 ast_debug(2, "MINIVM Counter %s/%s: Old value %d New value %d\n", directory, countername, old, counter);
02870 return counter;
02871 }
02872
02873
02874 static int minivm_counter_func_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
02875 {
02876 char *username, *domain, *countername;
02877 struct minivm_account *vmu = NULL;
02878 char userpath[BUFSIZ];
02879 int res;
02880
02881 *buf = '\0';
02882
02883 if (!(username = ast_strdupa(data))) {
02884 ast_log(LOG_WARNING, "Memory error!\n");
02885 return -1;
02886 }
02887 if ((countername = strchr(username, ':'))) {
02888 *countername = '\0';
02889 countername++;
02890 }
02891
02892 if ((domain = strchr(username, '@'))) {
02893 *domain = '\0';
02894 domain++;
02895 }
02896
02897
02898 if (ast_strlen_zero(username) && ast_strlen_zero(domain)) {
02899 ast_log(LOG_ERROR, "No account given\n");
02900 return -1;
02901 }
02902
02903 if (ast_strlen_zero(countername)) {
02904 ast_log(LOG_ERROR, "This function needs two arguments: Account:countername\n");
02905 return -1;
02906 }
02907
02908
02909 if (!ast_strlen_zero(username) && ast_strlen_zero(domain)) {
02910 domain = username;
02911 username = NULL;
02912 }
02913
02914
02915 if (!ast_strlen_zero(username) && !(vmu = find_account(domain, username, FALSE))) {
02916 ast_log(LOG_ERROR, "Minivm account does not exist: %s@%s\n", username, domain);
02917 return 0;
02918 }
02919
02920 create_dirpath(userpath, sizeof(userpath), domain, username, NULL);
02921
02922
02923 res = access_counter_file(userpath, countername, 0, 0);
02924 if (res >= 0)
02925 snprintf(buf, len, "%d", res);
02926 return 0;
02927 }
02928
02929
02930 static int minivm_counter_func_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
02931 {
02932 char *username, *domain, *countername, *operand;
02933 char userpath[BUFSIZ];
02934 struct minivm_account *vmu;
02935 int change = 0;
02936 int operation = 0;
02937
02938 if(!value)
02939 return -1;
02940 change = atoi(value);
02941
02942 if (!(username = ast_strdupa(data))) {
02943 ast_log(LOG_WARNING, "Memory error!\n");
02944 return -1;
02945 }
02946
02947 if ((countername = strchr(username, ':'))) {
02948 *countername = '\0';
02949 countername++;
02950 }
02951 if ((operand = strchr(countername, ':'))) {
02952 *operand = '\0';
02953 operand++;
02954 }
02955
02956 if ((domain = strchr(username, '@'))) {
02957 *domain = '\0';
02958 domain++;
02959 }
02960
02961
02962 if (ast_strlen_zero(username) && ast_strlen_zero(domain)) {
02963 ast_log(LOG_ERROR, "No account given\n");
02964 return -1;
02965 }
02966
02967
02968 if (!ast_strlen_zero(username) && ast_strlen_zero(domain)) {
02969 domain = username;
02970 username = NULL;
02971 }
02972
02973 if (ast_strlen_zero(operand) || ast_strlen_zero(countername)) {
02974 ast_log(LOG_ERROR, "Writing to this function requires three arguments: Account:countername:operand\n");
02975 return -1;
02976 }
02977
02978
02979 if (!ast_strlen_zero(username) && !(vmu = find_account(domain, username, FALSE))) {
02980 ast_log(LOG_ERROR, "Minivm account does not exist: %s@%s\n", username, domain);
02981 return 0;
02982 }
02983
02984 create_dirpath(userpath, sizeof(userpath), domain, username, NULL);
02985
02986 if (*operand == 'i')
02987 operation = 2;
02988 else if (*operand == 'd') {
02989 change = change * -1;
02990 operation = 2;
02991 } else if (*operand == 's')
02992 operation = 1;
02993 else {
02994 ast_log(LOG_ERROR, "Unknown operator: %s\n", operand);
02995 return -1;
02996 }
02997
02998
02999 access_counter_file(userpath, countername, change, operation);
03000 return 0;
03001 }
03002
03003
03004
03005 static struct ast_cli_entry cli_minivm[] = {
03006 AST_CLI_DEFINE(handle_minivm_show_users, "List defined mini-voicemail boxes"),
03007 AST_CLI_DEFINE(handle_minivm_show_zones, "List zone message formats"),
03008 AST_CLI_DEFINE(handle_minivm_list_templates, "List message templates"),
03009 AST_CLI_DEFINE(handle_minivm_reload, "Reload Mini-voicemail configuration"),
03010 AST_CLI_DEFINE(handle_minivm_show_stats, "Show some mini-voicemail statistics"),
03011 AST_CLI_DEFINE(handle_minivm_show_settings, "Show mini-voicemail general settings"),
03012 };
03013
03014 static struct ast_custom_function minivm_counter_function = {
03015 .name = "MINIVMCOUNTER",
03016 .synopsis = "Reads or sets counters for MiniVoicemail message",
03017 .syntax = "MINIVMCOUNTER(<account>:name[:operand])",
03018 .read = minivm_counter_func_read,
03019 .write = minivm_counter_func_write,
03020 .desc = "Valid operands for changing the value of a counter when assigning a value are:\n"
03021 "- i Increment by value\n"
03022 "- d Decrement by value\n"
03023 "- s Set to value\n"
03024 "\nThe counters never goes below zero.\n"
03025 "- The name of the counter is a string, up to 10 characters\n"
03026 "- If account is given and it exists, the counter is specific for the account\n"
03027 "- If account is a domain and the domain directory exists, counters are specific for a domain\n"
03028 "The operation is atomic and the counter is locked while changing the value\n"
03029 "\nThe counters are stored as text files in the minivm account directories. It might be better to use\n"
03030 "realtime functions if you are using a database to operate your Asterisk\n",
03031 };
03032
03033 static struct ast_custom_function minivm_account_function = {
03034 .name = "MINIVMACCOUNT",
03035 .synopsis = "Gets MiniVoicemail account information",
03036 .syntax = "MINIVMACCOUNT(<account>:item)",
03037 .read = minivm_account_func_read,
03038 .desc = "Valid items are:\n"
03039 "- path Path to account mailbox (if account exists, otherwise temporary mailbox)\n"
03040 "- hasaccount 1 if static Minivm account exists, 0 otherwise\n"
03041 "- fullname Full name of account owner\n"
03042 "- email Email address used for account\n"
03043 "- etemplate E-mail template for account (default template if none is configured)\n"
03044 "- ptemplate Pager template for account (default template if none is configured)\n"
03045 "- accountcode Account code for voicemail account\n"
03046 "- pincode Pin code for voicemail account\n"
03047 "- timezone Time zone for voicemail account\n"
03048 "- language Language for voicemail account\n"
03049 "- <channel variable name> Channel variable value (set in configuration for account)\n"
03050 "\n",
03051 };
03052
03053
03054 static int load_module(void)
03055 {
03056 int res;
03057
03058 res = ast_register_application(app_minivm_record, minivm_record_exec, synopsis_minivm_record, descrip_minivm_record);
03059 res = ast_register_application(app_minivm_greet, minivm_greet_exec, synopsis_minivm_greet, descrip_minivm_greet);
03060 res = ast_register_application(app_minivm_notify, minivm_notify_exec, synopsis_minivm_notify, descrip_minivm_notify);
03061 res = ast_register_application(app_minivm_delete, minivm_delete_exec, synopsis_minivm_delete, descrip_minivm_delete);
03062 res = ast_register_application(app_minivm_accmess, minivm_accmess_exec, synopsis_minivm_accmess, descrip_minivm_accmess);
03063
03064 ast_custom_function_register(&minivm_account_function);
03065 ast_custom_function_register(&minivm_counter_function);
03066 if (res)
03067 return(res);
03068
03069 if ((res = load_config(0)))
03070 return(res);
03071
03072 ast_cli_register_multiple(cli_minivm, sizeof(cli_minivm)/sizeof(cli_minivm[0]));
03073
03074
03075 snprintf(MVM_SPOOL_DIR, sizeof(MVM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
03076
03077 return res;
03078 }
03079
03080
03081 static int reload(void)
03082 {
03083 return(load_config(1));
03084 }
03085
03086
03087 static char *handle_minivm_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03088 {
03089
03090 switch (cmd) {
03091 case CLI_INIT:
03092 e->command = "minivm reload";
03093 e->usage =
03094 "Usage: minivm reload\n"
03095 " Reload mini-voicemail configuration and reset statistics\n";
03096 return NULL;
03097 case CLI_GENERATE:
03098 return NULL;
03099 }
03100
03101 reload();
03102 ast_cli(a->fd, "\n-- Mini voicemail re-configured \n");
03103 return CLI_SUCCESS;
03104 }
03105
03106
03107 static int unload_module(void)
03108 {
03109 int res;
03110
03111 res = ast_unregister_application(app_minivm_record);
03112 res |= ast_unregister_application(app_minivm_greet);
03113 res |= ast_unregister_application(app_minivm_notify);
03114 res |= ast_unregister_application(app_minivm_delete);
03115 res |= ast_unregister_application(app_minivm_accmess);
03116 ast_cli_unregister_multiple(cli_minivm, sizeof(cli_minivm)/sizeof(cli_minivm[0]));
03117 ast_custom_function_unregister(&minivm_account_function);
03118 ast_custom_function_unregister(&minivm_counter_function);
03119
03120 message_destroy_list();
03121 timezone_destroy_list();
03122 vmaccounts_destroy_list();
03123
03124 return res;
03125 }
03126
03127
03128 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Mini VoiceMail (A minimal Voicemail e-mail System)",
03129 .load = load_module,
03130 .unload = unload_module,
03131 .reload = reload,
03132 );