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