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