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