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