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