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: 413586 $")
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
01163 ast_str_reset(*end);
01164 ast_str_set(&tmp, -1, "=?%s?Q?", charset);
01165 for (; *start; start++) {
01166 int need_encoding = 0;
01167 if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
01168 need_encoding = 1;
01169 }
01170 if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
01171 (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
01172 (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
01173 (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
01174
01175 ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
01176 ast_str_set(&tmp, -1, "=?%s?Q?", charset);
01177 first_section = 0;
01178 }
01179 if (need_encoding && *start == ' ') {
01180 ast_str_append(&tmp, -1, "_");
01181 } else if (need_encoding) {
01182 ast_str_append(&tmp, -1, "=%hhX", *start);
01183 } else {
01184 ast_str_append(&tmp, -1, "%c", *start);
01185 }
01186 }
01187 ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
01188 return ast_str_buffer(*end);
01189 }
01190
01191
01192
01193
01194
01195
01196
01197
01198
01199 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
01200 {
01201 const char *ptr;
01202
01203
01204 ast_str_set(buf, maxlen, "\"");
01205 for (ptr = from; *ptr; ptr++) {
01206 if (*ptr == '"' || *ptr == '\\') {
01207 ast_str_append(buf, maxlen, "\\%c", *ptr);
01208 } else {
01209 ast_str_append(buf, maxlen, "%c", *ptr);
01210 }
01211 }
01212 ast_str_append(buf, maxlen, "\"");
01213
01214 return ast_str_buffer(*buf);
01215 }
01216
01217
01218
01219 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)
01220 {
01221 FILE *p = NULL;
01222 int pfd;
01223 char email[256] = "";
01224 char who[256] = "";
01225 char date[256];
01226 char bound[256];
01227 char fname[PATH_MAX];
01228 char dur[PATH_MAX];
01229 char tmp[80] = "/tmp/astmail-XXXXXX";
01230 char tmp2[PATH_MAX];
01231 char newtmp[PATH_MAX];
01232 struct timeval now;
01233 struct ast_tm tm;
01234 struct minivm_zone *the_zone = NULL;
01235 struct ast_channel *ast;
01236 char *finalfilename = "";
01237 struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
01238 char *fromaddress;
01239 char *fromemail;
01240
01241 if (!str1 || !str2) {
01242 ast_free(str1);
01243 ast_free(str2);
01244 return -1;
01245 }
01246
01247 if (type == MVM_MESSAGE_EMAIL) {
01248 if (vmu && !ast_strlen_zero(vmu->email)) {
01249 ast_copy_string(email, vmu->email, sizeof(email));
01250 } else if (!ast_strlen_zero(vmu->username) && !ast_strlen_zero(vmu->domain))
01251 snprintf(email, sizeof(email), "%s@%s", vmu->username, vmu->domain);
01252 } else if (type == MVM_MESSAGE_PAGE) {
01253 ast_copy_string(email, vmu->pager, sizeof(email));
01254 }
01255
01256 if (ast_strlen_zero(email)) {
01257 ast_log(LOG_WARNING, "No address to send message to.\n");
01258 ast_free(str1);
01259 ast_free(str2);
01260 return -1;
01261 }
01262
01263 ast_debug(3, "Sending mail to %s@%s - Using template %s\n", vmu->username, vmu->domain, template->name);
01264
01265 if (!strcmp(format, "wav49"))
01266 format = "WAV";
01267
01268
01269
01270 if (type == MVM_MESSAGE_EMAIL && (vmu->volgain < -.001 || vmu->volgain > .001) ) {
01271 char tmpcmd[PATH_MAX];
01272 int tmpfd;
01273
01274 ast_copy_string(newtmp, "/tmp/XXXXXX", sizeof(newtmp));
01275 ast_debug(3, "newtmp: %s\n", newtmp);
01276 tmpfd = mkstemp(newtmp);
01277 if (tmpfd < 0) {
01278 ast_log(LOG_WARNING, "Failed to create temporary file for volgain: %d\n", errno);
01279 ast_free(str1);
01280 ast_free(str2);
01281 return -1;
01282 }
01283 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, filename, format, newtmp, format);
01284 ast_safe_system(tmpcmd);
01285 close(tmpfd);
01286 finalfilename = newtmp;
01287 ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", filename, format, vmu->volgain, vmu->username);
01288 } else {
01289 finalfilename = ast_strdupa(filename);
01290 }
01291
01292
01293 snprintf(fname, sizeof(fname), "%s.%s", finalfilename, format);
01294
01295 if (template->attachment)
01296 ast_debug(1, "Attaching file '%s', format '%s', uservm is '%d'\n", finalfilename, format, attach_user_voicemail);
01297
01298
01299
01300 pfd = mkstemp(tmp);
01301 if (pfd > -1) {
01302 p = fdopen(pfd, "w");
01303 if (!p) {
01304 close(pfd);
01305 pfd = -1;
01306 }
01307 ast_debug(1, "Opening temp file for e-mail: %s\n", tmp);
01308 }
01309 if (!p) {
01310 ast_log(LOG_WARNING, "Unable to open temporary file '%s'\n", tmp);
01311 ast_free(str1);
01312 ast_free(str2);
01313 return -1;
01314 }
01315
01316 ast = ast_dummy_channel_alloc();
01317 if (!ast) {
01318 ast_free(str1);
01319 ast_free(str2);
01320 return -1;
01321 }
01322
01323 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
01324
01325
01326 if (!ast_strlen_zero(vmu->zonetag)) {
01327
01328 struct minivm_zone *z;
01329 AST_LIST_LOCK(&minivm_zones);
01330 AST_LIST_TRAVERSE(&minivm_zones, z, list) {
01331 if (strcmp(z->name, vmu->zonetag))
01332 continue;
01333 the_zone = z;
01334 }
01335 AST_LIST_UNLOCK(&minivm_zones);
01336 }
01337
01338 now = ast_tvnow();
01339 ast_localtime(&now, &tm, the_zone ? the_zone->timezone : NULL);
01340 ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
01341
01342
01343 fprintf(p, "Date: %s\n", date);
01344
01345
01346 ast_strftime(date, sizeof(date), template->dateformat, &tm);
01347
01348
01349
01350 prep_email_sub_vars(ast, vmu, cidnum, cidname, dur, date, counter);
01351
01352
01353
01354 fromemail = ast_strlen_zero(vmu->serveremail) ? template->serveremail : vmu->serveremail;
01355
01356
01357 fromaddress = ast_strlen_zero(template->fromaddress) ? "" : template->fromaddress;
01358
01359
01360 if (ast_strlen_zero(fromemail))
01361 fromemail = "asterisk";
01362
01363 if (strchr(fromemail, '@'))
01364 ast_copy_string(who, fromemail, sizeof(who));
01365 else {
01366 char host[MAXHOSTNAMELEN];
01367 gethostname(host, sizeof(host)-1);
01368 snprintf(who, sizeof(who), "%s@%s", fromemail, host);
01369 }
01370
01371 if (ast_strlen_zero(fromaddress)) {
01372 fprintf(p, "From: Asterisk PBX <%s>\n", who);
01373 } else {
01374 ast_debug(4, "Fromaddress template: %s\n", fromaddress);
01375 ast_str_substitute_variables(&str1, 0, ast, fromaddress);
01376 if (check_mime(ast_str_buffer(str1))) {
01377 int first_line = 1;
01378 char *ptr;
01379 ast_str_encode_mime(&str2, 0, template->charset, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
01380 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
01381 *ptr = '\0';
01382 fprintf(p, "%s %s\n", first_line ? "From:" : "", ast_str_buffer(str2));
01383 first_line = 0;
01384
01385 ast_str_set(&str2, 0, "%s", ptr + 1);
01386 }
01387 fprintf(p, "%s %s <%s>\n", first_line ? "From:" : "", ast_str_buffer(str2), who);
01388 } else {
01389 fprintf(p, "From: %s <%s>\n", ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
01390 }
01391 }
01392
01393 fprintf(p, "Message-ID: <Asterisk-%u-%s-%d-%s>\n", (unsigned int)ast_random(), vmu->username, (int)getpid(), who);
01394
01395 if (ast_strlen_zero(vmu->email)) {
01396 snprintf(email, sizeof(email), "%s@%s", vmu->username, vmu->domain);
01397 } else {
01398 ast_copy_string(email, vmu->email, sizeof(email));
01399 }
01400
01401 if (check_mime(vmu->fullname)) {
01402 int first_line = 1;
01403 char *ptr;
01404 ast_str_encode_mime(&str2, 0, template->charset, vmu->fullname, strlen("To: "), strlen(email) + 3);
01405 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
01406 *ptr = '\0';
01407 fprintf(p, "%s %s\n", first_line ? "To:" : "", ast_str_buffer(str2));
01408 first_line = 0;
01409
01410 ast_str_set(&str2, 0, "%s", ptr + 1);
01411 }
01412 fprintf(p, "%s %s <%s>\n", first_line ? "To:" : "", ast_str_buffer(str2), email);
01413 } else {
01414 fprintf(p, "To: %s <%s>\n", ast_str_quote(&str2, 0, vmu->fullname), email);
01415 }
01416
01417 if (!ast_strlen_zero(template->subject)) {
01418 ast_str_substitute_variables(&str1, 0, ast, template->subject);
01419 if (check_mime(ast_str_buffer(str1))) {
01420 int first_line = 1;
01421 char *ptr;
01422 ast_str_encode_mime(&str2, 0, template->charset, ast_str_buffer(str1), strlen("Subject: "), 0);
01423 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
01424 *ptr = '\0';
01425 fprintf(p, "%s %s\n", first_line ? "Subject:" : "", ast_str_buffer(str2));
01426 first_line = 0;
01427
01428 ast_str_set(&str2, 0, "%s", ptr + 1);
01429 }
01430 fprintf(p, "%s %s\n", first_line ? "Subject:" : "", ast_str_buffer(str2));
01431 } else {
01432 fprintf(p, "Subject: %s\n", ast_str_buffer(str1));
01433 }
01434 } else {
01435 fprintf(p, "Subject: New message in mailbox %s@%s\n", vmu->username, vmu->domain);
01436 ast_debug(1, "Using default subject for this email \n");
01437 }
01438
01439 if (option_debug > 2)
01440 fprintf(p, "X-Asterisk-debug: template %s user account %s@%s\n", template->name, vmu->username, vmu->domain);
01441 fprintf(p, "MIME-Version: 1.0\n");
01442
01443
01444 snprintf(bound, sizeof(bound), "voicemail_%s%d%u", vmu->username, (int)getpid(), (unsigned int)ast_random());
01445
01446 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
01447
01448 fprintf(p, "--%s\n", bound);
01449 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", template->charset);
01450 if (!ast_strlen_zero(template->body)) {
01451 ast_str_substitute_variables(&str1, 0, ast, template->body);
01452 ast_debug(3, "Message now: %s\n-----\n", ast_str_buffer(str1));
01453 fprintf(p, "%s\n", ast_str_buffer(str1));
01454 } else {
01455 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message \n"
01456 "in mailbox %s from %s, on %s so you might\n"
01457 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
01458 dur, vmu->username, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
01459 ast_debug(3, "Using default message body (no template)\n-----\n");
01460 }
01461
01462 if (template->attachment) {
01463 char *ctype = "audio/x-";
01464 ast_debug(3, "Attaching file to message: %s\n", fname);
01465 if (!strcasecmp(format, "ogg"))
01466 ctype = "application/";
01467
01468 fprintf(p, "--%s\n", bound);
01469 fprintf(p, "Content-Type: %s%s; name=\"voicemailmsg.%s\"\n", ctype, format, format);
01470 fprintf(p, "Content-Transfer-Encoding: base64\n");
01471 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
01472 fprintf(p, "Content-Disposition: attachment; filename=\"voicemail%s.%s\"\n\n", counter ? counter : "", format);
01473
01474 base_encode(fname, p);
01475 fprintf(p, "\n\n--%s--\n.\n", bound);
01476 }
01477 fclose(p);
01478 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", global_mailcmd, tmp, tmp);
01479 ast_safe_system(tmp2);
01480 ast_debug(1, "Sent message to %s with command '%s' - %s\n", vmu->email, global_mailcmd, template->attachment ? "(media attachment)" : "");
01481 ast_debug(3, "Actual command used: %s\n", tmp2);
01482 ast = ast_channel_unref(ast);
01483 ast_free(str1);
01484 ast_free(str2);
01485 return 0;
01486 }
01487
01488
01489
01490 static int make_dir(char *dest, int len, const char *domain, const char *username, const char *folder)
01491 {
01492 return snprintf(dest, len, "%s%s/%s%s%s", MVM_SPOOL_DIR, domain, username, ast_strlen_zero(folder) ? "" : "/", folder ? folder : "");
01493 }
01494
01495
01496
01497
01498
01499
01500
01501
01502
01503
01504 static int check_dirpath(char *dest, int len, char *domain, char *username, char *folder)
01505 {
01506 struct stat filestat;
01507 make_dir(dest, len, domain, username, folder ? folder : "");
01508 if (stat(dest, &filestat)== -1)
01509 return FALSE;
01510 else
01511 return TRUE;
01512 }
01513
01514
01515
01516
01517
01518
01519
01520
01521
01522
01523 static int create_dirpath(char *dest, int len, char *domain, char *username, char *folder)
01524 {
01525 int res;
01526 make_dir(dest, len, domain, username, folder);
01527 if ((res = ast_mkdir(dest, 0777))) {
01528 ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01529 return -1;
01530 }
01531 ast_debug(2, "Creating directory for %s@%s folder %s : %s\n", username, domain, folder, dest);
01532 return 0;
01533 }
01534
01535
01536
01537
01538
01539 static int invent_message(struct ast_channel *chan, char *domain, char *username, int busy, char *ecodes)
01540 {
01541 int res;
01542 char fn[PATH_MAX];
01543
01544 ast_debug(2, "Still preparing to play message ...\n");
01545
01546 snprintf(fn, sizeof(fn), "%s%s/%s/greet", MVM_SPOOL_DIR, domain, username);
01547
01548 if (ast_fileexists(fn, NULL, NULL) > 0) {
01549 res = ast_streamfile(chan, fn, chan->language);
01550 if (res)
01551 return -1;
01552 res = ast_waitstream(chan, ecodes);
01553 if (res)
01554 return res;
01555 } else {
01556 int numericusername = 1;
01557 char *i = username;
01558
01559 ast_debug(2, "No personal prompts. Using default prompt set for language\n");
01560
01561 while (*i) {
01562 ast_debug(2, "Numeric? Checking %c\n", *i);
01563 if (!isdigit(*i)) {
01564 numericusername = FALSE;
01565 break;
01566 }
01567 i++;
01568 }
01569
01570 if (numericusername) {
01571 if (ast_streamfile(chan, "vm-theperson", chan->language))
01572 return -1;
01573 if ((res = ast_waitstream(chan, ecodes)))
01574 return res;
01575
01576 res = ast_say_digit_str(chan, username, ecodes, chan->language);
01577 if (res)
01578 return res;
01579 } else {
01580 if (ast_streamfile(chan, "vm-theextensionis", chan->language))
01581 return -1;
01582 if ((res = ast_waitstream(chan, ecodes)))
01583 return res;
01584 }
01585 }
01586
01587 res = ast_streamfile(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language);
01588 if (res)
01589 return -1;
01590 res = ast_waitstream(chan, ecodes);
01591 return res;
01592 }
01593
01594
01595
01596 static int vm_delete(char *file)
01597 {
01598 int res;
01599
01600 ast_debug(1, "Deleting voicemail file %s\n", file);
01601
01602 res = unlink(file);
01603 res |= ast_filedelete(file, NULL);
01604 return res;
01605 }
01606
01607
01608
01609
01610 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
01611 int outsidecaller, struct minivm_account *vmu, int *duration, int *sound_duration, const char *unlockdir,
01612 signed char record_gain)
01613 {
01614 int cmd = 0;
01615 int max_attempts = 3;
01616 int attempts = 0;
01617 int recorded = 0;
01618 int message_exists = 0;
01619 signed char zero_gain = 0;
01620 char *acceptdtmf = "#";
01621 char *canceldtmf = "";
01622
01623
01624
01625
01626 if (duration == NULL) {
01627 ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
01628 return -1;
01629 }
01630
01631 cmd = '3';
01632
01633 while ((cmd >= 0) && (cmd != 't')) {
01634 switch (cmd) {
01635 case '1':
01636 ast_verb(3, "Saving message as is\n");
01637 ast_stream_and_wait(chan, "vm-msgsaved", "");
01638 cmd = 't';
01639 break;
01640 case '2':
01641
01642 ast_verb(3, "Reviewing the message\n");
01643 ast_streamfile(chan, recordfile, chan->language);
01644 cmd = ast_waitstream(chan, AST_DIGIT_ANY);
01645 break;
01646 case '3':
01647 message_exists = 0;
01648
01649 if (recorded == 1)
01650 ast_verb(3, "Re-recording the message\n");
01651 else
01652 ast_verb(3, "Recording the message\n");
01653 if (recorded && outsidecaller)
01654 cmd = ast_play_and_wait(chan, "beep");
01655 recorded = 1;
01656
01657 if (record_gain)
01658 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
01659 if (ast_test_flag(vmu, MVM_OPERATOR))
01660 canceldtmf = "0";
01661 cmd = ast_play_and_record_full(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, global_silencethreshold, global_maxsilence, unlockdir, acceptdtmf, canceldtmf);
01662 if (record_gain)
01663 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
01664 if (cmd == -1)
01665 return cmd;
01666 if (cmd == '0')
01667 break;
01668 else if (cmd == '*')
01669 break;
01670 else {
01671
01672 message_exists = 1;
01673 cmd = 0;
01674 }
01675 break;
01676 case '4':
01677 case '5':
01678 case '6':
01679 case '7':
01680 case '8':
01681 case '9':
01682 case '*':
01683 case '#':
01684 cmd = ast_play_and_wait(chan, "vm-sorry");
01685 break;
01686 case '0':
01687 if(!ast_test_flag(vmu, MVM_OPERATOR)) {
01688 cmd = ast_play_and_wait(chan, "vm-sorry");
01689 break;
01690 }
01691 if (message_exists || recorded) {
01692 cmd = ast_play_and_wait(chan, "vm-saveoper");
01693 if (!cmd)
01694 cmd = ast_waitfordigit(chan, 3000);
01695 if (cmd == '1') {
01696 ast_play_and_wait(chan, "vm-msgsaved");
01697 cmd = '0';
01698 } else {
01699 ast_play_and_wait(chan, "vm-deleted");
01700 vm_delete(recordfile);
01701 cmd = '0';
01702 }
01703 }
01704 return cmd;
01705 default:
01706
01707
01708
01709 if (outsidecaller && !ast_test_flag(vmu, MVM_REVIEW))
01710 return cmd;
01711 if (message_exists) {
01712 cmd = ast_play_and_wait(chan, "vm-review");
01713 } else {
01714 cmd = ast_play_and_wait(chan, "vm-torerecord");
01715 if (!cmd)
01716 cmd = ast_waitfordigit(chan, 600);
01717 }
01718
01719 if (!cmd && outsidecaller && ast_test_flag(vmu, MVM_OPERATOR)) {
01720 cmd = ast_play_and_wait(chan, "vm-reachoper");
01721 if (!cmd)
01722 cmd = ast_waitfordigit(chan, 600);
01723 }
01724 if (!cmd)
01725 cmd = ast_waitfordigit(chan, 6000);
01726 if (!cmd) {
01727 attempts++;
01728 }
01729 if (attempts > max_attempts) {
01730 cmd = 't';
01731 }
01732 }
01733 }
01734 if (outsidecaller)
01735 ast_play_and_wait(chan, "vm-goodbye");
01736 if (cmd == 't')
01737 cmd = 0;
01738 return cmd;
01739 }
01740
01741
01742 static void run_externnotify(struct ast_channel *chan, struct minivm_account *vmu)
01743 {
01744 char arguments[BUFSIZ];
01745
01746 if (ast_strlen_zero(vmu->externnotify) && ast_strlen_zero(global_externnotify))
01747 return;
01748
01749 snprintf(arguments, sizeof(arguments), "%s %s@%s %s %s&",
01750 ast_strlen_zero(vmu->externnotify) ? global_externnotify : vmu->externnotify,
01751 vmu->username, vmu->domain,
01752 (chan->caller.id.name.valid && chan->caller.id.name.str)
01753 ? chan->caller.id.name.str : "",
01754 (chan->caller.id.number.valid && chan->caller.id.number.str)
01755 ? chan->caller.id.number.str : "");
01756
01757 ast_debug(1, "Executing: %s\n", arguments);
01758 ast_safe_system(arguments);
01759 }
01760
01761
01762
01763 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)
01764 {
01765 char *stringp;
01766 struct minivm_template *etemplate;
01767 char *messageformat;
01768 int res = 0;
01769 char oldlocale[100];
01770 const char *counter;
01771
01772 if (!ast_strlen_zero(vmu->attachfmt)) {
01773 if (strstr(format, vmu->attachfmt)) {
01774 format = vmu->attachfmt;
01775 } else {
01776 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);
01777 }
01778 }
01779
01780 etemplate = message_template_find(vmu->etemplate);
01781 if (!etemplate)
01782 etemplate = message_template_find(templatename);
01783 if (!etemplate)
01784 etemplate = message_template_find("email-default");
01785
01786
01787 stringp = messageformat = ast_strdupa(format);
01788 strsep(&stringp, "|");
01789
01790 if (!ast_strlen_zero(etemplate->locale)) {
01791 char *new_locale;
01792 ast_copy_string(oldlocale, setlocale(LC_TIME, NULL), sizeof(oldlocale));
01793 ast_debug(2, "Changing locale from %s to %s\n", oldlocale, etemplate->locale);
01794 new_locale = setlocale(LC_TIME, etemplate->locale);
01795 if (new_locale == NULL) {
01796 ast_log(LOG_WARNING, "-_-_- Changing to new locale did not work. Locale: %s\n", etemplate->locale);
01797 }
01798 }
01799
01800
01801
01802
01803 ast_channel_lock(chan);
01804 if ((counter = pbx_builtin_getvar_helper(chan, "MVM_COUNTER"))) {
01805 counter = ast_strdupa(counter);
01806 }
01807 ast_channel_unlock(chan);
01808
01809 if (ast_strlen_zero(counter)) {
01810 ast_debug(2, "MVM_COUNTER not found\n");
01811 } else {
01812 ast_debug(2, "MVM_COUNTER found - will use it with value %s\n", counter);
01813 }
01814
01815 res = sendmail(etemplate, vmu, cidnum, cidname, filename, messageformat, duration, etemplate->attachment, MVM_MESSAGE_EMAIL, counter);
01816
01817 if (res == 0 && !ast_strlen_zero(vmu->pager)) {
01818
01819 etemplate = message_template_find(vmu->ptemplate);
01820 if (!etemplate)
01821 etemplate = message_template_find("pager-default");
01822 if (etemplate->locale) {
01823 ast_copy_string(oldlocale, setlocale(LC_TIME, ""), sizeof(oldlocale));
01824 setlocale(LC_TIME, etemplate->locale);
01825 }
01826
01827 res = sendmail(etemplate, vmu, cidnum, cidname, filename, messageformat, duration, etemplate->attachment, MVM_MESSAGE_PAGE, counter);
01828 }
01829
01830 ast_manager_event(chan, EVENT_FLAG_CALL, "MiniVoiceMail", "Action: SentNotification\rn\nMailbox: %s@%s\r\nCounter: %s\r\n", vmu->username, vmu->domain, counter);
01831
01832 run_externnotify(chan, vmu);
01833
01834 if (etemplate->locale) {
01835 setlocale(LC_TIME, oldlocale);
01836 }
01837 return res;
01838 }
01839
01840
01841
01842
01843 static int leave_voicemail(struct ast_channel *chan, char *username, struct leave_vm_options *options)
01844 {
01845 char tmptxtfile[PATH_MAX];
01846 char callerid[256];
01847 FILE *txt;
01848 int res = 0, txtdes;
01849 int duration = 0;
01850 int sound_duration = 0;
01851 char date[256];
01852 char tmpdir[PATH_MAX];
01853 char ext_context[256] = "";
01854 char fmt[80];
01855 char *domain;
01856 char tmp[256] = "";
01857 struct minivm_account *vmu;
01858 int userdir;
01859
01860 ast_copy_string(tmp, username, sizeof(tmp));
01861 username = tmp;
01862 domain = strchr(tmp, '@');
01863 if (domain) {
01864 *domain = '\0';
01865 domain++;
01866 }
01867
01868 if (!(vmu = find_account(domain, username, TRUE))) {
01869
01870 ast_log(LOG_ERROR, "Can't allocate temporary account for '%s@%s'\n", username, domain);
01871 pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "FAILED");
01872 return 0;
01873 }
01874
01875
01876 if (strcmp(vmu->domain, "localhost"))
01877 snprintf(ext_context, sizeof(ext_context), "%s@%s", username, vmu->domain);
01878 else
01879 ast_copy_string(ext_context, vmu->domain, sizeof(ext_context));
01880
01881
01882 if (ast_strlen_zero(vmu->attachfmt))
01883 ast_copy_string(fmt, default_vmformat, sizeof(fmt));
01884 else
01885 ast_copy_string(fmt, vmu->attachfmt, sizeof(fmt));
01886
01887 if (ast_strlen_zero(fmt)) {
01888 ast_log(LOG_WARNING, "No format for saving voicemail? Default %s\n", default_vmformat);
01889 pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "FAILED");
01890 return res;
01891 }
01892
01893 userdir = check_dirpath(tmpdir, sizeof(tmpdir), vmu->domain, username, "tmp");
01894
01895
01896 if (!userdir) {
01897 create_dirpath(tmpdir, sizeof(tmpdir), "0000_minivm_temp", "mediafiles", "");
01898 ast_debug(3, "Creating temporary directory %s\n", tmpdir);
01899 }
01900
01901
01902 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
01903
01904
01905 txtdes = mkstemp(tmptxtfile);
01906 if (txtdes < 0) {
01907 ast_log(LOG_ERROR, "Unable to create message file %s: %s\n", tmptxtfile, strerror(errno));
01908 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
01909 if (!res)
01910 res = ast_waitstream(chan, "");
01911 pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "FAILED");
01912 return res;
01913 }
01914
01915 if (res >= 0) {
01916
01917 res = ast_streamfile(chan, "beep", chan->language);
01918 if (!res)
01919 res = ast_waitstream(chan, "");
01920 }
01921
01922
01923
01924 ast_debug(2, "Open file for metadata: %s\n", tmptxtfile);
01925
01926 res = play_record_review(chan, NULL, tmptxtfile, global_vmmaxmessage, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain);
01927
01928 txt = fdopen(txtdes, "w+");
01929 if (!txt) {
01930 ast_log(LOG_WARNING, "Error opening text file for output\n");
01931 } else {
01932 struct ast_tm tm;
01933 struct timeval now = ast_tvnow();
01934 char timebuf[30];
01935 char logbuf[BUFSIZ];
01936 get_date(date, sizeof(date));
01937 ast_localtime(&now, &tm, NULL);
01938 ast_strftime(timebuf, sizeof(timebuf), "%H:%M:%S", &tm);
01939
01940 ast_callerid_merge(callerid, sizeof(callerid),
01941 S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
01942 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
01943 "Unknown");
01944 snprintf(logbuf, sizeof(logbuf),
01945
01946 "%s:%s:%s:%s:%d:%s:%s:%s:%s:%d:%s:%s\n",
01947 username,
01948 chan->context,
01949 chan->macrocontext,
01950 chan->exten,
01951 chan->priority,
01952 chan->name,
01953 callerid,
01954 date,
01955 timebuf,
01956 duration,
01957 duration < global_vmminmessage ? "IGNORED" : "OK",
01958 vmu->accountcode
01959 );
01960 fprintf(txt, "%s", logbuf);
01961 if (minivmlogfile) {
01962 ast_mutex_lock(&minivmloglock);
01963 fprintf(minivmlogfile, "%s", logbuf);
01964 ast_mutex_unlock(&minivmloglock);
01965 }
01966
01967 if (sound_duration < global_vmminmessage) {
01968 ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, global_vmminmessage);
01969 fclose(txt);
01970 ast_filedelete(tmptxtfile, NULL);
01971 unlink(tmptxtfile);
01972 pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "FAILED");
01973 return 0;
01974 }
01975 fclose(txt);
01976 if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
01977 ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
01978 unlink(tmptxtfile);
01979 pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "FAILED");
01980 if(ast_test_flag(vmu, MVM_ALLOCED))
01981 free_user(vmu);
01982 return 0;
01983 }
01984
01985
01986 pbx_builtin_setvar_helper(chan, "MVM_FILENAME", tmptxtfile);
01987 snprintf(timebuf, sizeof(timebuf), "%d", duration);
01988 pbx_builtin_setvar_helper(chan, "MVM_DURATION", timebuf);
01989 pbx_builtin_setvar_helper(chan, "MVM_FORMAT", fmt);
01990
01991 }
01992 global_stats.lastreceived = ast_tvnow();
01993 global_stats.receivedmessages++;
01994 #if 0
01995
01996 if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
01997 ast_filedelete(tmptxtfile, NULL);
01998
01999 ast_debug(2, "-_-_- Deleted audio file after notification :: %s \n", tmptxtfile);
02000 }
02001 #endif
02002
02003 if (res > 0)
02004 res = 0;
02005
02006 if(ast_test_flag(vmu, MVM_ALLOCED))
02007 free_user(vmu);
02008
02009 pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "SUCCESS");
02010 return res;
02011 }
02012
02013
02014
02015 static void queue_mwi_event(const char *mbx, const char *ctx, int urgent, int new, int old)
02016 {
02017 struct ast_event *event;
02018 char *mailbox, *context;
02019
02020 mailbox = ast_strdupa(mbx);
02021 context = ast_strdupa(ctx);
02022 if (ast_strlen_zero(context)) {
02023 context = "default";
02024 }
02025
02026 if (!(event = ast_event_new(AST_EVENT_MWI,
02027 AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
02028 AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
02029 AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
02030 AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
02031 AST_EVENT_IE_END))) {
02032 return;
02033 }
02034
02035 ast_event_queue_and_cache(event);
02036 }
02037
02038
02039
02040 static int minivm_mwi_exec(struct ast_channel *chan, const char *data)
02041 {
02042 int argc;
02043 char *argv[4];
02044 int res = 0;
02045 char *tmpptr;
02046 char tmp[PATH_MAX];
02047 char *mailbox;
02048 char *domain;
02049 if (ast_strlen_zero(data)) {
02050 ast_log(LOG_ERROR, "Minivm needs at least an account argument \n");
02051 return -1;
02052 }
02053 tmpptr = ast_strdupa((char *)data);
02054 argc = ast_app_separate_args(tmpptr, ',', argv, ARRAY_LEN(argv));
02055 if (argc < 4) {
02056 ast_log(LOG_ERROR, "%d arguments passed to MiniVM_MWI, need 4.\n", argc);
02057 return -1;
02058 }
02059 ast_copy_string(tmp, argv[0], sizeof(tmp));
02060 mailbox = tmp;
02061 domain = strchr(tmp, '@');
02062 if (domain) {
02063 *domain = '\0';
02064 domain++;
02065 }
02066 if (ast_strlen_zero(domain) || ast_strlen_zero(mailbox)) {
02067 ast_log(LOG_ERROR, "Need mailbox@context as argument. Sorry. Argument 0 %s\n", argv[0]);
02068 return -1;
02069 }
02070 queue_mwi_event(mailbox, domain, atoi(argv[1]), atoi(argv[2]), atoi(argv[3]));
02071
02072 return res;
02073 }
02074
02075
02076
02077
02078 static int minivm_notify_exec(struct ast_channel *chan, const char *data)
02079 {
02080 int argc;
02081 char *argv[2];
02082 int res = 0;
02083 char tmp[PATH_MAX];
02084 char *domain;
02085 char *tmpptr;
02086 struct minivm_account *vmu;
02087 char *username;
02088 const char *template = "";
02089 const char *filename;
02090 const char *format;
02091 const char *duration_string;
02092
02093 if (ast_strlen_zero(data)) {
02094 ast_log(LOG_ERROR, "Minivm needs at least an account argument \n");
02095 return -1;
02096 }
02097 tmpptr = ast_strdupa((char *)data);
02098 argc = ast_app_separate_args(tmpptr, ',', argv, ARRAY_LEN(argv));
02099
02100 if (argc == 2 && !ast_strlen_zero(argv[1]))
02101 template = argv[1];
02102
02103 ast_copy_string(tmp, argv[0], sizeof(tmp));
02104 username = tmp;
02105 domain = strchr(tmp, '@');
02106 if (domain) {
02107 *domain = '\0';
02108 domain++;
02109 }
02110 if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
02111 ast_log(LOG_ERROR, "Need username@domain as argument. Sorry. Argument 0 %s\n", argv[0]);
02112 return -1;
02113 }
02114
02115 if(!(vmu = find_account(domain, username, TRUE))) {
02116
02117 ast_log(LOG_WARNING, "Could not allocate temporary memory for '%s@%s'\n", username, domain);
02118 pbx_builtin_setvar_helper(chan, "MVM_NOTIFY_STATUS", "FAILED");
02119 return -1;
02120 }
02121
02122 ast_channel_lock(chan);
02123 if ((filename = pbx_builtin_getvar_helper(chan, "MVM_FILENAME"))) {
02124 filename = ast_strdupa(filename);
02125 }
02126 ast_channel_unlock(chan);
02127
02128 if (!ast_strlen_zero(filename)) {
02129 ast_channel_lock(chan);
02130 if ((format = pbx_builtin_getvar_helper(chan, "MVM_FORMAT"))) {
02131 format = ast_strdupa(format);
02132 }
02133 if ((duration_string = pbx_builtin_getvar_helper(chan, "MVM_DURATION"))) {
02134 duration_string = ast_strdupa(duration_string);
02135 }
02136 ast_channel_unlock(chan);
02137 res = notify_new_message(chan, template, vmu, filename, atoi(duration_string),
02138 format,
02139 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
02140 S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL));
02141 }
02142
02143 pbx_builtin_setvar_helper(chan, "MVM_NOTIFY_STATUS", res == 0 ? "SUCCESS" : "FAILED");
02144
02145
02146 if(ast_test_flag(vmu, MVM_ALLOCED))
02147 free_user(vmu);
02148
02149
02150
02151 return res;
02152
02153 }
02154
02155
02156
02157 static int minivm_record_exec(struct ast_channel *chan, const char *data)
02158 {
02159 int res = 0;
02160 char *tmp;
02161 struct leave_vm_options leave_options;
02162 int argc;
02163 char *argv[2];
02164 struct ast_flags flags = { 0 };
02165 char *opts[OPT_ARG_ARRAY_SIZE];
02166
02167 memset(&leave_options, 0, sizeof(leave_options));
02168
02169
02170 if (chan->_state != AST_STATE_UP)
02171 ast_answer(chan);
02172
02173 if (ast_strlen_zero(data)) {
02174 ast_log(LOG_ERROR, "Minivm needs at least an account argument \n");
02175 return -1;
02176 }
02177 tmp = ast_strdupa((char *)data);
02178 argc = ast_app_separate_args(tmp, ',', argv, ARRAY_LEN(argv));
02179 if (argc == 2) {
02180 if (ast_app_parse_options(minivm_app_options, &flags, opts, argv[1])) {
02181 return -1;
02182 }
02183 ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING );
02184 if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
02185 int gain;
02186
02187 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
02188 ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
02189 return -1;
02190 } else
02191 leave_options.record_gain = (signed char) gain;
02192 }
02193 }
02194
02195
02196 res = leave_voicemail(chan, argv[0], &leave_options);
02197
02198 if (res == ERROR_LOCK_PATH) {
02199 ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
02200 pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "FAILED");
02201 res = 0;
02202 }
02203 pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "SUCCESS");
02204
02205 return res;
02206 }
02207
02208
02209
02210 static int minivm_greet_exec(struct ast_channel *chan, const char *data)
02211 {
02212 struct leave_vm_options leave_options = { 0, '\0'};
02213 int argc;
02214 char *argv[2];
02215 struct ast_flags flags = { 0 };
02216 char *opts[OPT_ARG_ARRAY_SIZE];
02217 int res = 0;
02218 int ausemacro = 0;
02219 int ousemacro = 0;
02220 int ouseexten = 0;
02221 char tmp[PATH_MAX];
02222 char dest[PATH_MAX];
02223 char prefile[PATH_MAX] = "";
02224 char tempfile[PATH_MAX] = "";
02225 char ext_context[256] = "";
02226 char *domain;
02227 char ecodes[16] = "#";
02228 char *tmpptr;
02229 struct minivm_account *vmu;
02230 char *username = argv[0];
02231
02232 if (ast_strlen_zero(data)) {
02233 ast_log(LOG_ERROR, "Minivm needs at least an account argument \n");
02234 return -1;
02235 }
02236 tmpptr = ast_strdupa((char *)data);
02237 argc = ast_app_separate_args(tmpptr, ',', argv, ARRAY_LEN(argv));
02238
02239 if (argc == 2) {
02240 if (ast_app_parse_options(minivm_app_options, &flags, opts, argv[1]))
02241 return -1;
02242 ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING );
02243 }
02244
02245 ast_copy_string(tmp, argv[0], sizeof(tmp));
02246 username = tmp;
02247 domain = strchr(tmp, '@');
02248 if (domain) {
02249 *domain = '\0';
02250 domain++;
02251 }
02252 if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
02253 ast_log(LOG_ERROR, "Need username@domain as argument. Sorry. Argument: %s\n", argv[0]);
02254 return -1;
02255 }
02256 ast_debug(1, "Trying to find configuration for user %s in domain %s\n", username, domain);
02257
02258 if (!(vmu = find_account(domain, username, TRUE))) {
02259 ast_log(LOG_ERROR, "Could not allocate memory. \n");
02260 return -1;
02261 }
02262
02263
02264 if (chan->_state != AST_STATE_UP)
02265 ast_answer(chan);
02266
02267
02268 if (strcmp(vmu->domain, "localhost"))
02269 snprintf(ext_context, sizeof(ext_context), "%s@%s", username, vmu->domain);
02270 else
02271 ast_copy_string(ext_context, vmu->domain, sizeof(ext_context));
02272
02273 if (ast_test_flag(&leave_options, OPT_BUSY_GREETING)) {
02274 res = check_dirpath(dest, sizeof(dest), vmu->domain, username, "busy");
02275 if (res)
02276 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", MVM_SPOOL_DIR, vmu->domain, username);
02277 } else if (ast_test_flag(&leave_options, OPT_UNAVAIL_GREETING)) {
02278 res = check_dirpath(dest, sizeof(dest), vmu->domain, username, "unavail");
02279 if (res)
02280 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", MVM_SPOOL_DIR, vmu->domain, username);
02281 }
02282
02283 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", MVM_SPOOL_DIR, vmu->domain, username);
02284 if (!(res = check_dirpath(dest, sizeof(dest), vmu->domain, username, "temp"))) {
02285 ast_debug(2, "Temporary message directory does not exist, using default (%s)\n", tempfile);
02286 ast_copy_string(prefile, tempfile, sizeof(prefile));
02287 }
02288 ast_debug(2, "Preparing to play message ...\n");
02289
02290
02291 if (ast_test_flag(vmu, MVM_OPERATOR)) {
02292 if (!ast_strlen_zero(vmu->exit)) {
02293 if (ast_exists_extension(chan, vmu->exit, "o", 1,
02294 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
02295 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
02296 ouseexten = 1;
02297 }
02298 } else if (ast_exists_extension(chan, chan->context, "o", 1,
02299 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
02300 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
02301 ouseexten = 1;
02302 }
02303 else if (!ast_strlen_zero(chan->macrocontext)
02304 && ast_exists_extension(chan, chan->macrocontext, "o", 1,
02305 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
02306 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
02307 ousemacro = 1;
02308 }
02309 }
02310
02311 if (!ast_strlen_zero(vmu->exit)) {
02312 if (ast_exists_extension(chan, vmu->exit, "a", 1,
02313 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
02314 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
02315 }
02316 } else if (ast_exists_extension(chan, chan->context, "a", 1,
02317 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
02318 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
02319 } else if (!ast_strlen_zero(chan->macrocontext)
02320 && ast_exists_extension(chan, chan->macrocontext, "a", 1,
02321 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
02322 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
02323 ausemacro = 1;
02324 }
02325
02326 res = 0;
02327
02328 if (!ast_strlen_zero(prefile)) {
02329 if (ast_streamfile(chan, prefile, chan->language) > -1)
02330 res = ast_waitstream(chan, ecodes);
02331 } else {
02332 ast_debug(2, "%s doesn't exist, doing what we can\n", prefile);
02333 res = invent_message(chan, vmu->domain, username, ast_test_flag(&leave_options, OPT_BUSY_GREETING), ecodes);
02334 }
02335 if (res < 0) {
02336 ast_debug(2, "Hang up during prefile playback\n");
02337 pbx_builtin_setvar_helper(chan, "MVM_GREET_STATUS", "FAILED");
02338 if(ast_test_flag(vmu, MVM_ALLOCED))
02339 free_user(vmu);
02340 return -1;
02341 }
02342 if (res == '#') {
02343
02344 ast_set_flag(&leave_options, OPT_SILENT);
02345 res = 0;
02346 }
02347 if (!res && !ast_test_flag(&leave_options, OPT_SILENT)) {
02348 res = ast_streamfile(chan, SOUND_INTRO, chan->language);
02349 if (!res)
02350 res = ast_waitstream(chan, ecodes);
02351 if (res == '#') {
02352 ast_set_flag(&leave_options, OPT_SILENT);
02353 res = 0;
02354 }
02355 }
02356 if (res > 0)
02357 ast_stopstream(chan);
02358
02359
02360 if (res == '*') {
02361 chan->exten[0] = 'a';
02362 chan->exten[1] = '\0';
02363 if (!ast_strlen_zero(vmu->exit)) {
02364 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
02365 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
02366 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
02367 }
02368 chan->priority = 0;
02369 pbx_builtin_setvar_helper(chan, "MVM_GREET_STATUS", "USEREXIT");
02370 res = 0;
02371 } else if (res == '0') {
02372 if(ouseexten || ousemacro) {
02373 chan->exten[0] = 'o';
02374 chan->exten[1] = '\0';
02375 if (!ast_strlen_zero(vmu->exit)) {
02376 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
02377 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
02378 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
02379 }
02380 ast_play_and_wait(chan, "transfer");
02381 chan->priority = 0;
02382 pbx_builtin_setvar_helper(chan, "MVM_GREET_STATUS", "USEREXIT");
02383 }
02384 res = 0;
02385 } else if (res < 0) {
02386 pbx_builtin_setvar_helper(chan, "MVM_GREET_STATUS", "FAILED");
02387 res = -1;
02388 } else
02389 pbx_builtin_setvar_helper(chan, "MVM_GREET_STATUS", "SUCCESS");
02390
02391 if(ast_test_flag(vmu, MVM_ALLOCED))
02392 free_user(vmu);
02393
02394
02395
02396 return res;
02397
02398 }
02399
02400
02401
02402 static int minivm_delete_exec(struct ast_channel *chan, const char *data)
02403 {
02404 int res = 0;
02405 char filename[BUFSIZ];
02406
02407 if (!ast_strlen_zero(data)) {
02408 ast_copy_string(filename, (char *) data, sizeof(filename));
02409 } else {
02410 ast_channel_lock(chan);
02411 ast_copy_string(filename, pbx_builtin_getvar_helper(chan, "MVM_FILENAME"), sizeof(filename));
02412 ast_channel_unlock(chan);
02413 }
02414
02415 if (ast_strlen_zero(filename)) {
02416 ast_log(LOG_ERROR, "No filename given in application arguments or channel variable MVM_FILENAME\n");
02417 return res;
02418 }
02419
02420
02421
02422 if (ast_fileexists(filename, NULL, NULL) > 0) {
02423 res = vm_delete(filename);
02424 if (res) {
02425 ast_debug(2, "Can't delete file: %s\n", filename);
02426 pbx_builtin_setvar_helper(chan, "MVM_DELETE_STATUS", "FAILED");
02427 } else {
02428 ast_debug(2, "Deleted voicemail file :: %s \n", filename);
02429 pbx_builtin_setvar_helper(chan, "MVM_DELETE_STATUS", "SUCCESS");
02430 }
02431 } else {
02432 ast_debug(2, "Filename does not exist: %s\n", filename);
02433 pbx_builtin_setvar_helper(chan, "MVM_DELETE_STATUS", "FAILED");
02434 }
02435
02436 return res;
02437 }
02438
02439
02440 static int minivm_accmess_exec(struct ast_channel *chan, const char *data)
02441 {
02442 int argc = 0;
02443 char *argv[2];
02444 char filename[PATH_MAX];
02445 char tmp[PATH_MAX];
02446 char *domain;
02447 char *tmpptr = NULL;
02448 struct minivm_account *vmu;
02449 char *username;
02450 struct ast_flags flags = { 0 };
02451 char *opts[OPT_ARG_ARRAY_SIZE];
02452 int error = FALSE;
02453 char *message = NULL;
02454 char *prompt = NULL;
02455 int duration;
02456
02457 if (ast_strlen_zero(data)) {
02458 ast_log(LOG_ERROR, "MinivmAccmess needs at least two arguments: account and option\n");
02459 error = TRUE;
02460 } else {
02461 tmpptr = ast_strdupa((char *)data);
02462 argc = ast_app_separate_args(tmpptr, ',', argv, ARRAY_LEN(argv));
02463 }
02464
02465 if (argc <=1) {
02466 ast_log(LOG_ERROR, "MinivmAccmess needs at least two arguments: account and option\n");
02467 error = TRUE;
02468 }
02469 if (!error && strlen(argv[1]) > 1) {
02470 ast_log(LOG_ERROR, "MinivmAccmess can only handle one option at a time. Bad option string: %s\n", argv[1]);
02471 error = TRUE;
02472 }
02473
02474 if (!error && ast_app_parse_options(minivm_accmess_options, &flags, opts, argv[1])) {
02475 ast_log(LOG_ERROR, "Can't parse option %s\n", argv[1]);
02476 error = TRUE;
02477 }
02478
02479 if (error) {
02480 pbx_builtin_setvar_helper(chan, "MVM_ACCMESS_STATUS", "FAILED");
02481 return -1;
02482 }
02483
02484 ast_copy_string(tmp, argv[0], sizeof(tmp));
02485 username = tmp;
02486 domain = strchr(tmp, '@');
02487 if (domain) {
02488 *domain = '\0';
02489 domain++;
02490 }
02491 if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
02492 ast_log(LOG_ERROR, "Need username@domain as argument. Sorry. Argument 0 %s\n", argv[0]);
02493 pbx_builtin_setvar_helper(chan, "MVM_ACCMESS_STATUS", "FAILED");
02494 return -1;
02495 }
02496
02497 if(!(vmu = find_account(domain, username, TRUE))) {
02498
02499 ast_log(LOG_WARNING, "Could not allocate temporary memory for '%s@%s'\n", username, domain);
02500 pbx_builtin_setvar_helper(chan, "MVM_ACCMESS_STATUS", "FAILED");
02501 return -1;
02502 }
02503
02504
02505 if (chan->_state != AST_STATE_UP)
02506 ast_answer(chan);
02507
02508
02509 if (ast_test_flag(&flags, OPT_BUSY_GREETING)) {
02510 message = "busy";
02511 prompt = "vm-rec-busy";
02512 } else if (ast_test_flag(&flags, OPT_UNAVAIL_GREETING)) {
02513 message = "unavailable";
02514 prompt = "vm-rec-unv";
02515 } else if (ast_test_flag(&flags, OPT_TEMP_GREETING)) {
02516 message = "temp";
02517 prompt = "vm-rec-temp";
02518 } else if (ast_test_flag(&flags, OPT_NAME_GREETING)) {
02519 message = "greet";
02520 prompt = "vm-rec-name";
02521 }
02522 snprintf(filename,sizeof(filename), "%s%s/%s/%s", MVM_SPOOL_DIR, vmu->domain, vmu->username, message);
02523
02524 play_record_review(chan, prompt, filename, global_maxgreet, default_vmformat, 0, vmu, &duration, NULL, NULL, FALSE);
02525
02526 ast_debug(1, "Recorded new %s message in %s (duration %d)\n", message, filename, duration);
02527
02528 if(ast_test_flag(vmu, MVM_ALLOCED))
02529 free_user(vmu);
02530
02531 pbx_builtin_setvar_helper(chan, "MVM_ACCMESS_STATUS", "SUCCESS");
02532
02533
02534 return 0;
02535 }
02536
02537
02538 static int create_vmaccount(char *name, struct ast_variable *var, int realtime)
02539 {
02540 struct minivm_account *vmu;
02541 char *domain;
02542 char *username;
02543 char accbuf[BUFSIZ];
02544
02545 ast_debug(3, "Creating %s account for [%s]\n", realtime ? "realtime" : "static", name);
02546
02547 ast_copy_string(accbuf, name, sizeof(accbuf));
02548 username = accbuf;
02549 domain = strchr(accbuf, '@');
02550 if (domain) {
02551 *domain = '\0';
02552 domain++;
02553 }
02554 if (ast_strlen_zero(domain)) {
02555 ast_log(LOG_ERROR, "No domain given for mini-voicemail account %s. Not configured.\n", name);
02556 return 0;
02557 }
02558
02559 ast_debug(3, "Creating static account for user %s domain %s\n", username, domain);
02560
02561
02562 vmu = ast_calloc(1, sizeof(*vmu));
02563 if (!vmu)
02564 return 0;
02565
02566 ast_copy_string(vmu->domain, domain, sizeof(vmu->domain));
02567 ast_copy_string(vmu->username, username, sizeof(vmu->username));
02568
02569 populate_defaults(vmu);
02570
02571 ast_debug(3, "...Configuring account %s\n", name);
02572
02573 while (var) {
02574 ast_debug(3, "Configuring %s = \"%s\" for account %s\n", var->name, var->value, name);
02575 if (!strcasecmp(var->name, "serveremail")) {
02576 ast_copy_string(vmu->serveremail, var->value, sizeof(vmu->serveremail));
02577 } else if (!strcasecmp(var->name, "email")) {
02578 ast_copy_string(vmu->email, var->value, sizeof(vmu->email));
02579 } else if (!strcasecmp(var->name, "accountcode")) {
02580 ast_copy_string(vmu->accountcode, var->value, sizeof(vmu->accountcode));
02581 } else if (!strcasecmp(var->name, "pincode")) {
02582 ast_copy_string(vmu->pincode, var->value, sizeof(vmu->pincode));
02583 } else if (!strcasecmp(var->name, "domain")) {
02584 ast_copy_string(vmu->domain, var->value, sizeof(vmu->domain));
02585 } else if (!strcasecmp(var->name, "language")) {
02586 ast_copy_string(vmu->language, var->value, sizeof(vmu->language));
02587 } else if (!strcasecmp(var->name, "timezone")) {
02588 ast_copy_string(vmu->zonetag, var->value, sizeof(vmu->zonetag));
02589 } else if (!strcasecmp(var->name, "externnotify")) {
02590 ast_copy_string(vmu->externnotify, var->value, sizeof(vmu->externnotify));
02591 } else if (!strcasecmp(var->name, "etemplate")) {
02592 ast_copy_string(vmu->etemplate, var->value, sizeof(vmu->etemplate));
02593 } else if (!strcasecmp(var->name, "ptemplate")) {
02594 ast_copy_string(vmu->ptemplate, var->value, sizeof(vmu->ptemplate));
02595 } else if (!strcasecmp(var->name, "fullname")) {
02596 ast_copy_string(vmu->fullname, var->value, sizeof(vmu->fullname));
02597 } else if (!strcasecmp(var->name, "setvar")) {
02598 char *varval;
02599 char *varname = ast_strdupa(var->value);
02600 struct ast_variable *tmpvar;
02601
02602 if ((varval = strchr(varname, '='))) {
02603 *varval = '\0';
02604 varval++;
02605 if ((tmpvar = ast_variable_new(varname, varval, ""))) {
02606 tmpvar->next = vmu->chanvars;
02607 vmu->chanvars = tmpvar;
02608 }
02609 }
02610 } else if (!strcasecmp(var->name, "pager")) {
02611 ast_copy_string(vmu->pager, var->value, sizeof(vmu->pager));
02612 } else if (!strcasecmp(var->name, "volgain")) {
02613 sscanf(var->value, "%30lf", &vmu->volgain);
02614 } else {
02615 ast_log(LOG_ERROR, "Unknown configuration option for minivm account %s : %s\n", name, var->name);
02616 }
02617 var = var->next;
02618 }
02619 ast_debug(3, "...Linking account %s\n", name);
02620
02621 AST_LIST_LOCK(&minivm_accounts);
02622 AST_LIST_INSERT_TAIL(&minivm_accounts, vmu, list);
02623 AST_LIST_UNLOCK(&minivm_accounts);
02624
02625 global_stats.voicemailaccounts++;
02626
02627 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)" : "");
02628 return 0;
02629 }
02630
02631
02632 static void free_zone(struct minivm_zone *z)
02633 {
02634 ast_free(z);
02635 }
02636
02637
02638 static void timezone_destroy_list(void)
02639 {
02640 struct minivm_zone *this;
02641
02642 AST_LIST_LOCK(&minivm_zones);
02643 while ((this = AST_LIST_REMOVE_HEAD(&minivm_zones, list)))
02644 free_zone(this);
02645
02646 AST_LIST_UNLOCK(&minivm_zones);
02647 }
02648
02649
02650 static int timezone_add(const char *zonename, const char *config)
02651 {
02652 struct minivm_zone *newzone;
02653 char *msg_format, *timezone_str;
02654
02655 newzone = ast_calloc(1, sizeof(*newzone));
02656 if (newzone == NULL)
02657 return 0;
02658
02659 msg_format = ast_strdupa(config);
02660
02661 timezone_str = strsep(&msg_format, "|");
02662 if (!msg_format) {
02663 ast_log(LOG_WARNING, "Invalid timezone definition : %s\n", zonename);
02664 ast_free(newzone);
02665 return 0;
02666 }
02667
02668 ast_copy_string(newzone->name, zonename, sizeof(newzone->name));
02669 ast_copy_string(newzone->timezone, timezone_str, sizeof(newzone->timezone));
02670 ast_copy_string(newzone->msg_format, msg_format, sizeof(newzone->msg_format));
02671
02672 AST_LIST_LOCK(&minivm_zones);
02673 AST_LIST_INSERT_TAIL(&minivm_zones, newzone, list);
02674 AST_LIST_UNLOCK(&minivm_zones);
02675
02676 global_stats.timezones++;
02677
02678 return 0;
02679 }
02680
02681
02682 static char *message_template_parse_filebody(const char *filename) {
02683 char buf[BUFSIZ * 6];
02684 char readbuf[BUFSIZ];
02685 char filenamebuf[BUFSIZ];
02686 char *writepos;
02687 char *messagebody;
02688 FILE *fi;
02689 int lines = 0;
02690
02691 if (ast_strlen_zero(filename))
02692 return NULL;
02693 if (*filename == '/')
02694 ast_copy_string(filenamebuf, filename, sizeof(filenamebuf));
02695 else
02696 snprintf(filenamebuf, sizeof(filenamebuf), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
02697
02698 if (!(fi = fopen(filenamebuf, "r"))) {
02699 ast_log(LOG_ERROR, "Can't read message template from file: %s\n", filenamebuf);
02700 return NULL;
02701 }
02702 writepos = buf;
02703 while (fgets(readbuf, sizeof(readbuf), fi)) {
02704 lines ++;
02705 if (writepos != buf) {
02706 *writepos = '\n';
02707 writepos++;
02708 }
02709 ast_copy_string(writepos, readbuf, sizeof(buf) - (writepos - buf));
02710 writepos += strlen(readbuf) - 1;
02711 }
02712 fclose(fi);
02713 messagebody = ast_calloc(1, strlen(buf + 1));
02714 ast_copy_string(messagebody, buf, strlen(buf) + 1);
02715 ast_debug(4, "---> Size of allocation %d\n", (int) strlen(buf + 1) );
02716 ast_debug(4, "---> Done reading message template : \n%s\n---- END message template--- \n", messagebody);
02717
02718 return messagebody;
02719 }
02720
02721
02722 static char *message_template_parse_emailbody(const char *configuration)
02723 {
02724 char *tmpread, *tmpwrite;
02725 char *emailbody = ast_strdup(configuration);
02726
02727
02728 tmpread = tmpwrite = emailbody;
02729 while ((tmpwrite = strchr(tmpread,'\\'))) {
02730 int len = strlen("\n");
02731 switch (tmpwrite[1]) {
02732 case 'n':
02733 memmove(tmpwrite + len, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
02734 strncpy(tmpwrite, "\n", len);
02735 break;
02736 case 't':
02737 memmove(tmpwrite + len, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
02738 strncpy(tmpwrite, "\t", len);
02739 break;
02740 default:
02741 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
02742 }
02743 tmpread = tmpwrite + len;
02744 }
02745 return emailbody;
02746 }
02747
02748
02749 static int apply_general_options(struct ast_variable *var)
02750 {
02751 int error = 0;
02752
02753 while (var) {
02754
02755 if (!strcmp(var->name, "mailcmd")) {
02756 ast_copy_string(global_mailcmd, var->value, sizeof(global_mailcmd));
02757 } else if (!strcmp(var->name, "maxgreet")) {
02758 global_maxgreet = atoi(var->value);
02759 } else if (!strcmp(var->name, "maxsilence")) {
02760 global_maxsilence = atoi(var->value);
02761 if (global_maxsilence > 0)
02762 global_maxsilence *= 1000;
02763 } else if (!strcmp(var->name, "logfile")) {
02764 if (!ast_strlen_zero(var->value) ) {
02765 if(*(var->value) == '/')
02766 ast_copy_string(global_logfile, var->value, sizeof(global_logfile));
02767 else
02768 snprintf(global_logfile, sizeof(global_logfile), "%s/%s", ast_config_AST_LOG_DIR, var->value);
02769 }
02770 } else if (!strcmp(var->name, "externnotify")) {
02771
02772 ast_copy_string(global_externnotify, var->value, sizeof(global_externnotify));
02773 } else if (!strcmp(var->name, "silencetreshold")) {
02774
02775 global_silencethreshold = atoi(var->value);
02776 } else if (!strcmp(var->name, "maxmessage")) {
02777 int x;
02778 if (sscanf(var->value, "%30d", &x) == 1) {
02779 global_vmmaxmessage = x;
02780 } else {
02781 error ++;
02782 ast_log(LOG_WARNING, "Invalid max message time length\n");
02783 }
02784 } else if (!strcmp(var->name, "minmessage")) {
02785 int x;
02786 if (sscanf(var->value, "%30d", &x) == 1) {
02787 global_vmminmessage = x;
02788 if (global_maxsilence <= global_vmminmessage)
02789 ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
02790 } else {
02791 error ++;
02792 ast_log(LOG_WARNING, "Invalid min message time length\n");
02793 }
02794 } else if (!strcmp(var->name, "format")) {
02795 ast_copy_string(default_vmformat, var->value, sizeof(default_vmformat));
02796 } else if (!strcmp(var->name, "review")) {
02797 ast_set2_flag((&globalflags), ast_true(var->value), MVM_REVIEW);
02798 } else if (!strcmp(var->name, "operator")) {
02799 ast_set2_flag((&globalflags), ast_true(var->value), MVM_OPERATOR);
02800 }
02801 var = var->next;
02802 }
02803 return error;
02804 }
02805
02806
02807 static int load_config(int reload)
02808 {
02809 struct ast_config *cfg;
02810 struct ast_variable *var;
02811 char *cat;
02812 const char *chanvar;
02813 int error = 0;
02814 struct minivm_template *template;
02815 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
02816
02817 cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
02818 if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
02819 return 0;
02820 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
02821 ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
02822 return 0;
02823 }
02824
02825 ast_mutex_lock(&minivmlock);
02826
02827
02828 message_destroy_list();
02829 timezone_destroy_list();
02830 vmaccounts_destroy_list();
02831 ast_debug(2, "Destroyed memory objects...\n");
02832
02833
02834 global_externnotify[0] = '\0';
02835 global_logfile[0] = '\0';
02836 global_vmmaxmessage = 2000;
02837 global_maxgreet = 2000;
02838 global_vmminmessage = 0;
02839 strcpy(global_mailcmd, SENDMAIL);
02840 global_maxsilence = 0;
02841 global_saydurationminfo = 2;
02842 ast_copy_string(default_vmformat, "wav", sizeof(default_vmformat));
02843 ast_set2_flag((&globalflags), FALSE, MVM_REVIEW);
02844 ast_set2_flag((&globalflags), FALSE, MVM_OPERATOR);
02845
02846 memset(&global_stats, 0, sizeof(global_stats));
02847 global_stats.reset = ast_tvnow();
02848
02849 global_silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
02850
02851
02852 if (!cfg) {
02853 ast_log(LOG_WARNING, "Failed to load configuration file. Module activated with default settings.\n");
02854 ast_mutex_unlock(&minivmlock);
02855 return 0;
02856 }
02857
02858 ast_debug(2, "Loaded configuration file, now parsing\n");
02859
02860
02861
02862 cat = ast_category_browse(cfg, NULL);
02863 while (cat) {
02864 ast_debug(3, "Found configuration section [%s]\n", cat);
02865 if (!strcasecmp(cat, "general")) {
02866
02867 error += apply_general_options(ast_variable_browse(cfg, cat));
02868 } else if (!strncasecmp(cat, "template-", 9)) {
02869
02870 char *name = cat + 9;
02871
02872
02873 error += message_template_build(name, ast_variable_browse(cfg, cat));
02874 } else {
02875 var = ast_variable_browse(cfg, cat);
02876 if (!strcasecmp(cat, "zonemessages")) {
02877
02878 while (var) {
02879 timezone_add(var->name, var->value);
02880 var = var->next;
02881 }
02882 } else {
02883
02884 error += create_vmaccount(cat, var, FALSE);
02885 }
02886 }
02887
02888 cat = ast_category_browse(cfg, cat);
02889 }
02890
02891
02892 message_template_build("email-default", NULL);
02893 template = message_template_find("email-default");
02894
02895
02896 if ((chanvar = ast_variable_retrieve(cfg, "general", "emaildateformat")))
02897 ast_copy_string(template->dateformat, chanvar, sizeof(template->dateformat));
02898 if ((chanvar = ast_variable_retrieve(cfg, "general", "emailfromstring")))
02899 ast_copy_string(template->fromaddress, chanvar, sizeof(template->fromaddress));
02900 if ((chanvar = ast_variable_retrieve(cfg, "general", "emailaaddress")))
02901 ast_copy_string(template->serveremail, chanvar, sizeof(template->serveremail));
02902 if ((chanvar = ast_variable_retrieve(cfg, "general", "emailcharset")))
02903 ast_copy_string(template->charset, chanvar, sizeof(template->charset));
02904 if ((chanvar = ast_variable_retrieve(cfg, "general", "emailsubject")))
02905 ast_copy_string(template->subject, chanvar, sizeof(template->subject));
02906 if ((chanvar = ast_variable_retrieve(cfg, "general", "emailbody")))
02907 template->body = message_template_parse_emailbody(chanvar);
02908 template->attachment = TRUE;
02909
02910 message_template_build("pager-default", NULL);
02911 template = message_template_find("pager-default");
02912 if ((chanvar = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
02913 ast_copy_string(template->fromaddress, chanvar, sizeof(template->fromaddress));
02914 if ((chanvar = ast_variable_retrieve(cfg, "general", "pageraddress")))
02915 ast_copy_string(template->serveremail, chanvar, sizeof(template->serveremail));
02916 if ((chanvar = ast_variable_retrieve(cfg, "general", "pagercharset")))
02917 ast_copy_string(template->charset, chanvar, sizeof(template->charset));
02918 if ((chanvar = ast_variable_retrieve(cfg, "general", "pagersubject")))
02919 ast_copy_string(template->subject, chanvar,sizeof(template->subject));
02920 if ((chanvar = ast_variable_retrieve(cfg, "general", "pagerbody")))
02921 template->body = message_template_parse_emailbody(chanvar);
02922 template->attachment = FALSE;
02923
02924 if (error)
02925 ast_log(LOG_ERROR, "--- A total of %d errors found in mini-voicemail configuration\n", error);
02926
02927 ast_mutex_unlock(&minivmlock);
02928 ast_config_destroy(cfg);
02929
02930
02931 if(minivmlogfile)
02932 fclose(minivmlogfile);
02933
02934
02935 if(!ast_strlen_zero(global_logfile)) {
02936 minivmlogfile = fopen(global_logfile, "a");
02937 if(!minivmlogfile)
02938 ast_log(LOG_ERROR, "Failed to open minivm log file %s : %s\n", global_logfile, strerror(errno));
02939 if (minivmlogfile)
02940 ast_debug(3, "Opened log file %s \n", global_logfile);
02941 }
02942
02943 return 0;
02944 }
02945
02946
02947 static char *handle_minivm_list_templates(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02948 {
02949 struct minivm_template *this;
02950 #define HVLT_OUTPUT_FORMAT "%-15s %-10s %-10s %-15.15s %-50s\n"
02951 int count = 0;
02952
02953 switch (cmd) {
02954 case CLI_INIT:
02955 e->command = "minivm list templates";
02956 e->usage =
02957 "Usage: minivm list templates\n"
02958 " Lists message templates for e-mail, paging and IM\n";
02959 return NULL;
02960 case CLI_GENERATE:
02961 return NULL;
02962 }
02963
02964 if (a->argc > 3)
02965 return CLI_SHOWUSAGE;
02966
02967 AST_LIST_LOCK(&message_templates);
02968 if (AST_LIST_EMPTY(&message_templates)) {
02969 ast_cli(a->fd, "There are no message templates defined\n");
02970 AST_LIST_UNLOCK(&message_templates);
02971 return CLI_FAILURE;
02972 }
02973 ast_cli(a->fd, HVLT_OUTPUT_FORMAT, "Template name", "Charset", "Locale", "Attach media", "Subject");
02974 ast_cli(a->fd, HVLT_OUTPUT_FORMAT, "-------------", "-------", "------", "------------", "-------");
02975 AST_LIST_TRAVERSE(&message_templates, this, list) {
02976 ast_cli(a->fd, HVLT_OUTPUT_FORMAT, this->name,
02977 this->charset ? this->charset : "-",
02978 this->locale ? this->locale : "-",
02979 this->attachment ? "Yes" : "No",
02980 this->subject ? this->subject : "-");
02981 count++;
02982 }
02983 AST_LIST_UNLOCK(&message_templates);
02984 ast_cli(a->fd, "\n * Total: %d minivoicemail message templates\n", count);
02985 return CLI_SUCCESS;
02986 }
02987
02988 static char *complete_minivm_show_users(const char *line, const char *word, int pos, int state)
02989 {
02990 int which = 0;
02991 int wordlen;
02992 struct minivm_account *vmu;
02993 const char *domain = "";
02994
02995
02996 if (pos > 4)
02997 return NULL;
02998 if (pos == 3)
02999 return (state == 0) ? ast_strdup("for") : NULL;
03000 wordlen = strlen(word);
03001 AST_LIST_TRAVERSE(&minivm_accounts, vmu, list) {
03002 if (!strncasecmp(word, vmu->domain, wordlen)) {
03003 if (domain && strcmp(domain, vmu->domain) && ++which > state)
03004 return ast_strdup(vmu->domain);
03005
03006 domain = vmu->domain;
03007 }
03008 }
03009 return NULL;
03010 }
03011
03012
03013 static char *handle_minivm_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03014 {
03015 struct minivm_account *vmu;
03016 #define HMSU_OUTPUT_FORMAT "%-23s %-15s %-15s %-10s %-10s %-50s\n"
03017 int count = 0;
03018
03019 switch (cmd) {
03020 case CLI_INIT:
03021 e->command = "minivm list accounts";
03022 e->usage =
03023 "Usage: minivm list accounts\n"
03024 " Lists all mailboxes currently set up\n";
03025 return NULL;
03026 case CLI_GENERATE:
03027 return complete_minivm_show_users(a->line, a->word, a->pos, a->n);
03028 }
03029
03030 if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
03031 return CLI_SHOWUSAGE;
03032 if ((a->argc == 5) && strcmp(a->argv[3],"for"))
03033 return CLI_SHOWUSAGE;
03034
03035 AST_LIST_LOCK(&minivm_accounts);
03036 if (AST_LIST_EMPTY(&minivm_accounts)) {
03037 ast_cli(a->fd, "There are no voicemail users currently defined\n");
03038 AST_LIST_UNLOCK(&minivm_accounts);
03039 return CLI_FAILURE;
03040 }
03041 ast_cli(a->fd, HMSU_OUTPUT_FORMAT, "User", "E-Template", "P-template", "Zone", "Format", "Full name");
03042 ast_cli(a->fd, HMSU_OUTPUT_FORMAT, "----", "----------", "----------", "----", "------", "---------");
03043 AST_LIST_TRAVERSE(&minivm_accounts, vmu, list) {
03044 char tmp[256] = "";
03045 if ((a->argc == 3) || ((a->argc == 5) && !strcmp(a->argv[4], vmu->domain))) {
03046 count++;
03047 snprintf(tmp, sizeof(tmp), "%s@%s", vmu->username, vmu->domain);
03048 ast_cli(a->fd, HMSU_OUTPUT_FORMAT, tmp, vmu->etemplate ? vmu->etemplate : "-",
03049 vmu->ptemplate ? vmu->ptemplate : "-",
03050 vmu->zonetag ? vmu->zonetag : "-",
03051 vmu->attachfmt ? vmu->attachfmt : "-",
03052 vmu->fullname);
03053 }
03054 }
03055 AST_LIST_UNLOCK(&minivm_accounts);
03056 ast_cli(a->fd, "\n * Total: %d minivoicemail accounts\n", count);
03057 return CLI_SUCCESS;
03058 }
03059
03060
03061 static char *handle_minivm_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03062 {
03063 struct minivm_zone *zone;
03064 #define HMSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
03065 char *res = CLI_SUCCESS;
03066
03067 switch (cmd) {
03068 case CLI_INIT:
03069 e->command = "minivm list zones";
03070 e->usage =
03071 "Usage: minivm list zones\n"
03072 " Lists zone message formats\n";
03073 return NULL;
03074 case CLI_GENERATE:
03075 return NULL;
03076 }
03077
03078 if (a->argc != e->args)
03079 return CLI_SHOWUSAGE;
03080
03081 AST_LIST_LOCK(&minivm_zones);
03082 if (!AST_LIST_EMPTY(&minivm_zones)) {
03083 ast_cli(a->fd, HMSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
03084 ast_cli(a->fd, HMSZ_OUTPUT_FORMAT, "----", "--------", "--------------");
03085 AST_LIST_TRAVERSE(&minivm_zones, zone, list) {
03086 ast_cli(a->fd, HMSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
03087 }
03088 } else {
03089 ast_cli(a->fd, "There are no voicemail zones currently defined\n");
03090 res = CLI_FAILURE;
03091 }
03092 AST_LIST_UNLOCK(&minivm_zones);
03093
03094 return res;
03095 }
03096
03097
03098 static char *handle_minivm_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03099 {
03100 switch (cmd) {
03101 case CLI_INIT:
03102 e->command = "minivm show settings";
03103 e->usage =
03104 "Usage: minivm show settings\n"
03105 " Display Mini-Voicemail general settings\n";
03106 return NULL;
03107 case CLI_GENERATE:
03108 return NULL;
03109 }
03110
03111 ast_cli(a->fd, "* Mini-Voicemail general settings\n");
03112 ast_cli(a->fd, " -------------------------------\n");
03113 ast_cli(a->fd, "\n");
03114 ast_cli(a->fd, " Mail command (shell): %s\n", global_mailcmd);
03115 ast_cli(a->fd, " Max silence: %d\n", global_maxsilence);
03116 ast_cli(a->fd, " Silence threshold: %d\n", global_silencethreshold);
03117 ast_cli(a->fd, " Max message length (secs): %d\n", global_vmmaxmessage);
03118 ast_cli(a->fd, " Min message length (secs): %d\n", global_vmminmessage);
03119 ast_cli(a->fd, " Default format: %s\n", default_vmformat);
03120 ast_cli(a->fd, " Extern notify (shell): %s\n", global_externnotify);
03121 ast_cli(a->fd, " Logfile: %s\n", global_logfile[0] ? global_logfile : "<disabled>");
03122 ast_cli(a->fd, " Operator exit: %s\n", ast_test_flag(&globalflags, MVM_OPERATOR) ? "Yes" : "No");
03123 ast_cli(a->fd, " Message review: %s\n", ast_test_flag(&globalflags, MVM_REVIEW) ? "Yes" : "No");
03124
03125 ast_cli(a->fd, "\n");
03126 return CLI_SUCCESS;
03127 }
03128
03129
03130 static char *handle_minivm_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03131 {
03132 struct ast_tm timebuf;
03133 char buf[BUFSIZ];
03134
03135 switch (cmd) {
03136
03137 case CLI_INIT:
03138 e->command = "minivm show stats";
03139 e->usage =
03140 "Usage: minivm show stats\n"
03141 " Display Mini-Voicemail counters\n";
03142 return NULL;
03143 case CLI_GENERATE:
03144 return NULL;
03145 }
03146
03147 ast_cli(a->fd, "* Mini-Voicemail statistics\n");
03148 ast_cli(a->fd, " -------------------------\n");
03149 ast_cli(a->fd, "\n");
03150 ast_cli(a->fd, " Voicemail accounts: %5d\n", global_stats.voicemailaccounts);
03151 ast_cli(a->fd, " Templates: %5d\n", global_stats.templates);
03152 ast_cli(a->fd, " Timezones: %5d\n", global_stats.timezones);
03153 if (global_stats.receivedmessages == 0) {
03154 ast_cli(a->fd, " Received messages since last reset: <none>\n");
03155 } else {
03156 ast_cli(a->fd, " Received messages since last reset: %d\n", global_stats.receivedmessages);
03157 ast_localtime(&global_stats.lastreceived, &timebuf, NULL);
03158 ast_strftime(buf, sizeof(buf), "%a %b %e %r %Z %Y", &timebuf);
03159 ast_cli(a->fd, " Last received voicemail: %s\n", buf);
03160 }
03161 ast_localtime(&global_stats.reset, &timebuf, NULL);
03162 ast_strftime(buf, sizeof(buf), "%a %b %e %r %Z %Y", &timebuf);
03163 ast_cli(a->fd, " Last reset: %s\n", buf);
03164
03165 ast_cli(a->fd, "\n");
03166 return CLI_SUCCESS;
03167 }
03168
03169
03170 static int minivm_account_func_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
03171 {
03172 struct minivm_account *vmu;
03173 char *username, *domain, *colname;
03174
03175 username = ast_strdupa(data);
03176
03177 if ((colname = strchr(username, ':'))) {
03178 *colname = '\0';
03179 colname++;
03180 } else {
03181 colname = "path";
03182 }
03183 if ((domain = strchr(username, '@'))) {
03184 *domain = '\0';
03185 domain++;
03186 }
03187 if (ast_strlen_zero(username) || ast_strlen_zero(domain)) {
03188 ast_log(LOG_ERROR, "This function needs a username and a domain: username@domain\n");
03189 return 0;
03190 }
03191
03192 if (!(vmu = find_account(domain, username, TRUE)))
03193 return 0;
03194
03195 if (!strcasecmp(colname, "hasaccount")) {
03196 ast_copy_string(buf, (ast_test_flag(vmu, MVM_ALLOCED) ? "0" : "1"), len);
03197 } else if (!strcasecmp(colname, "fullname")) {
03198 ast_copy_string(buf, vmu->fullname, len);
03199 } else if (!strcasecmp(colname, "email")) {
03200 if (!ast_strlen_zero(vmu->email))
03201 ast_copy_string(buf, vmu->email, len);
03202 else
03203 snprintf(buf, len, "%s@%s", vmu->username, vmu->domain);
03204 } else if (!strcasecmp(colname, "pager")) {
03205 ast_copy_string(buf, vmu->pager, len);
03206 } else if (!strcasecmp(colname, "etemplate")) {
03207 if (!ast_strlen_zero(vmu->etemplate))
03208 ast_copy_string(buf, vmu->etemplate, len);
03209 else
03210 ast_copy_string(buf, "email-default", len);
03211 } else if (!strcasecmp(colname, "language")) {
03212 ast_copy_string(buf, vmu->language, len);
03213 } else if (!strcasecmp(colname, "timezone")) {
03214 ast_copy_string(buf, vmu->zonetag, len);
03215 } else if (!strcasecmp(colname, "ptemplate")) {
03216 if (!ast_strlen_zero(vmu->ptemplate))
03217 ast_copy_string(buf, vmu->ptemplate, len);
03218 else
03219 ast_copy_string(buf, "email-default", len);
03220 } else if (!strcasecmp(colname, "accountcode")) {
03221 ast_copy_string(buf, vmu->accountcode, len);
03222 } else if (!strcasecmp(colname, "pincode")) {
03223 ast_copy_string(buf, vmu->pincode, len);
03224 } else if (!strcasecmp(colname, "path")) {
03225 check_dirpath(buf, len, vmu->domain, vmu->username, NULL);
03226 } else {
03227 struct ast_variable *var;
03228
03229 for (var = vmu->chanvars ; var ; var = var->next)
03230 if (!strcmp(var->name, colname)) {
03231 ast_copy_string(buf, var->value, len);
03232 break;
03233 }
03234 }
03235
03236 if(ast_test_flag(vmu, MVM_ALLOCED))
03237 free_user(vmu);
03238
03239 return 0;
03240 }
03241
03242
03243
03244
03245
03246
03247 static int vm_lock_path(const char *path)
03248 {
03249 switch (ast_lock_path(path)) {
03250 case AST_LOCK_TIMEOUT:
03251 return -1;
03252 default:
03253 return 0;
03254 }
03255 }
03256
03257
03258
03259
03260
03261
03262
03263
03264 static int access_counter_file(char *directory, char *countername, int value, int operand)
03265 {
03266 char filename[BUFSIZ];
03267 char readbuf[BUFSIZ];
03268 FILE *counterfile;
03269 int old = 0, counter = 0;
03270
03271
03272 if (vm_lock_path(directory)) {
03273 return -1;
03274 }
03275 snprintf(filename, sizeof(filename), "%s/%s.counter", directory, countername);
03276 if (operand != 1) {
03277 counterfile = fopen(filename, "r");
03278 if (counterfile) {
03279 if(fgets(readbuf, sizeof(readbuf), counterfile)) {
03280 ast_debug(3, "Read this string from counter file: %s\n", readbuf);
03281 old = counter = atoi(readbuf);
03282 }
03283 fclose(counterfile);
03284 }
03285 }
03286 switch (operand) {
03287 case 0:
03288 ast_unlock_path(directory);
03289 ast_debug(2, "MINIVM Counter %s/%s: Value %d\n", directory, countername, counter);
03290 return counter;
03291 break;
03292 case 1:
03293 counter = value;
03294 break;
03295 case 2:
03296 counter += value;
03297 if (counter < 0)
03298 counter = 0;
03299 break;
03300 }
03301
03302
03303 counterfile = fopen(filename, "w");
03304 if (!counterfile) {
03305 ast_log(LOG_ERROR, "Could not open counter file for writing : %s - %s\n", filename, strerror(errno));
03306 ast_unlock_path(directory);
03307 return -1;
03308 }
03309 fprintf(counterfile, "%d\n\n", counter);
03310 fclose(counterfile);
03311 ast_unlock_path(directory);
03312 ast_debug(2, "MINIVM Counter %s/%s: Old value %d New value %d\n", directory, countername, old, counter);
03313 return counter;
03314 }
03315
03316
03317 static int minivm_counter_func_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
03318 {
03319 char *username, *domain, *countername;
03320 struct minivm_account *vmu = NULL;
03321 char userpath[BUFSIZ];
03322 int res;
03323
03324 *buf = '\0';
03325
03326 username = ast_strdupa(data);
03327
03328 if ((countername = strchr(username, ':'))) {
03329 *countername = '\0';
03330 countername++;
03331 }
03332
03333 if ((domain = strchr(username, '@'))) {
03334 *domain = '\0';
03335 domain++;
03336 }
03337
03338
03339 if (ast_strlen_zero(username) && ast_strlen_zero(domain)) {
03340 ast_log(LOG_ERROR, "No account given\n");
03341 return -1;
03342 }
03343
03344 if (ast_strlen_zero(countername)) {
03345 ast_log(LOG_ERROR, "This function needs two arguments: Account:countername\n");
03346 return -1;
03347 }
03348
03349
03350 if (!ast_strlen_zero(username) && ast_strlen_zero(domain)) {
03351 domain = username;
03352 username = NULL;
03353 }
03354
03355
03356 if (!ast_strlen_zero(username) && !(vmu = find_account(domain, username, FALSE))) {
03357 ast_log(LOG_ERROR, "Minivm account does not exist: %s@%s\n", username, domain);
03358 return 0;
03359 }
03360
03361 create_dirpath(userpath, sizeof(userpath), domain, username, NULL);
03362
03363
03364 res = access_counter_file(userpath, countername, 0, 0);
03365 if (res >= 0)
03366 snprintf(buf, len, "%d", res);
03367 return 0;
03368 }
03369
03370
03371 static int minivm_counter_func_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
03372 {
03373 char *username, *domain, *countername, *operand;
03374 char userpath[BUFSIZ];
03375 struct minivm_account *vmu;
03376 int change = 0;
03377 int operation = 0;
03378
03379 if(!value)
03380 return -1;
03381 change = atoi(value);
03382
03383 username = ast_strdupa(data);
03384
03385 if ((countername = strchr(username, ':'))) {
03386 *countername = '\0';
03387 countername++;
03388 }
03389 if ((operand = strchr(countername, ':'))) {
03390 *operand = '\0';
03391 operand++;
03392 }
03393
03394 if ((domain = strchr(username, '@'))) {
03395 *domain = '\0';
03396 domain++;
03397 }
03398
03399
03400 if (ast_strlen_zero(username) && ast_strlen_zero(domain)) {
03401 ast_log(LOG_ERROR, "No account given\n");
03402 return -1;
03403 }
03404
03405
03406 if (!ast_strlen_zero(username) && ast_strlen_zero(domain)) {
03407 domain = username;
03408 username = NULL;
03409 }
03410
03411 if (ast_strlen_zero(operand) || ast_strlen_zero(countername)) {
03412 ast_log(LOG_ERROR, "Writing to this function requires three arguments: Account:countername:operand\n");
03413 return -1;
03414 }
03415
03416
03417 if (!ast_strlen_zero(username) && !(vmu = find_account(domain, username, FALSE))) {
03418 ast_log(LOG_ERROR, "Minivm account does not exist: %s@%s\n", username, domain);
03419 return 0;
03420 }
03421
03422 create_dirpath(userpath, sizeof(userpath), domain, username, NULL);
03423
03424 if (*operand == 'i')
03425 operation = 2;
03426 else if (*operand == 'd') {
03427 change = change * -1;
03428 operation = 2;
03429 } else if (*operand == 's')
03430 operation = 1;
03431 else {
03432 ast_log(LOG_ERROR, "Unknown operator: %s\n", operand);
03433 return -1;
03434 }
03435
03436
03437 access_counter_file(userpath, countername, change, operation);
03438 return 0;
03439 }
03440
03441
03442
03443 static struct ast_cli_entry cli_minivm[] = {
03444 AST_CLI_DEFINE(handle_minivm_show_users, "List defined mini-voicemail boxes"),
03445 AST_CLI_DEFINE(handle_minivm_show_zones, "List zone message formats"),
03446 AST_CLI_DEFINE(handle_minivm_list_templates, "List message templates"),
03447 AST_CLI_DEFINE(handle_minivm_reload, "Reload Mini-voicemail configuration"),
03448 AST_CLI_DEFINE(handle_minivm_show_stats, "Show some mini-voicemail statistics"),
03449 AST_CLI_DEFINE(handle_minivm_show_settings, "Show mini-voicemail general settings"),
03450 };
03451
03452 static struct ast_custom_function minivm_counter_function = {
03453 .name = "MINIVMCOUNTER",
03454 .read = minivm_counter_func_read,
03455 .write = minivm_counter_func_write,
03456 };
03457
03458 static struct ast_custom_function minivm_account_function = {
03459 .name = "MINIVMACCOUNT",
03460 .read = minivm_account_func_read,
03461 };
03462
03463
03464 static int load_module(void)
03465 {
03466 int res;
03467
03468 res = ast_register_application_xml(app_minivm_record, minivm_record_exec);
03469 res = ast_register_application_xml(app_minivm_greet, minivm_greet_exec);
03470 res = ast_register_application_xml(app_minivm_notify, minivm_notify_exec);
03471 res = ast_register_application_xml(app_minivm_delete, minivm_delete_exec);
03472 res = ast_register_application_xml(app_minivm_accmess, minivm_accmess_exec);
03473 res = ast_register_application_xml(app_minivm_mwi, minivm_mwi_exec);
03474
03475 ast_custom_function_register(&minivm_account_function);
03476 ast_custom_function_register(&minivm_counter_function);
03477 if (res)
03478 return(res);
03479
03480 if ((res = load_config(0)))
03481 return(res);
03482
03483 ast_cli_register_multiple(cli_minivm, ARRAY_LEN(cli_minivm));
03484
03485
03486 snprintf(MVM_SPOOL_DIR, sizeof(MVM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
03487
03488 return res;
03489 }
03490
03491
03492 static int reload(void)
03493 {
03494 return(load_config(1));
03495 }
03496
03497
03498 static char *handle_minivm_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03499 {
03500
03501 switch (cmd) {
03502 case CLI_INIT:
03503 e->command = "minivm reload";
03504 e->usage =
03505 "Usage: minivm reload\n"
03506 " Reload mini-voicemail configuration and reset statistics\n";
03507 return NULL;
03508 case CLI_GENERATE:
03509 return NULL;
03510 }
03511
03512 reload();
03513 ast_cli(a->fd, "\n-- Mini voicemail re-configured \n");
03514 return CLI_SUCCESS;
03515 }
03516
03517
03518 static int unload_module(void)
03519 {
03520 int res;
03521
03522 res = ast_unregister_application(app_minivm_record);
03523 res |= ast_unregister_application(app_minivm_greet);
03524 res |= ast_unregister_application(app_minivm_notify);
03525 res |= ast_unregister_application(app_minivm_delete);
03526 res |= ast_unregister_application(app_minivm_accmess);
03527 res |= ast_unregister_application(app_minivm_mwi);
03528
03529 ast_cli_unregister_multiple(cli_minivm, ARRAY_LEN(cli_minivm));
03530 ast_custom_function_unregister(&minivm_account_function);
03531 ast_custom_function_unregister(&minivm_counter_function);
03532
03533 message_destroy_list();
03534 timezone_destroy_list();
03535 vmaccounts_destroy_list();
03536
03537 return res;
03538 }
03539
03540
03541 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Mini VoiceMail (A minimal Voicemail e-mail System)",
03542 .load = load_module,
03543 .unload = unload_module,
03544 .reload = reload,
03545 );