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 #include "asterisk.h"
00029
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 403917 $")
00031
00032 #include <sys/stat.h>
00033
00034 #include "asterisk/module.h"
00035 #include "asterisk/channel.h"
00036 #include "asterisk/pbx.h"
00037 #include "asterisk/utils.h"
00038 #include "asterisk/app.h"
00039 #include "asterisk/file.h"
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
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247 static int env_read(struct ast_channel *chan, const char *cmd, char *data,
00248 char *buf, size_t len)
00249 {
00250 char *ret = NULL;
00251
00252 *buf = '\0';
00253
00254 if (data)
00255 ret = getenv(data);
00256
00257 if (ret)
00258 ast_copy_string(buf, ret, len);
00259
00260 return 0;
00261 }
00262
00263 static int env_write(struct ast_channel *chan, const char *cmd, char *data,
00264 const char *value)
00265 {
00266 if (!ast_strlen_zero(data) && strncmp(data, "AST_", 4)) {
00267 if (!ast_strlen_zero(value)) {
00268 setenv(data, value, 1);
00269 } else {
00270 unsetenv(data);
00271 }
00272 }
00273
00274 return 0;
00275 }
00276
00277 static int stat_read(struct ast_channel *chan, const char *cmd, char *data,
00278 char *buf, size_t len)
00279 {
00280 char *action;
00281 struct stat s;
00282
00283 ast_copy_string(buf, "0", len);
00284
00285 action = strsep(&data, ",");
00286 if (stat(data, &s)) {
00287 return 0;
00288 } else {
00289 switch (*action) {
00290 case 'e':
00291 strcpy(buf, "1");
00292 break;
00293 case 's':
00294 snprintf(buf, len, "%d", (unsigned int) s.st_size);
00295 break;
00296 case 'f':
00297 snprintf(buf, len, "%d", S_ISREG(s.st_mode) ? 1 : 0);
00298 break;
00299 case 'd':
00300 snprintf(buf, len, "%d", S_ISDIR(s.st_mode) ? 1 : 0);
00301 break;
00302 case 'M':
00303 snprintf(buf, len, "%d", (int) s.st_mtime);
00304 break;
00305 case 'A':
00306 snprintf(buf, len, "%d", (int) s.st_mtime);
00307 break;
00308 case 'C':
00309 snprintf(buf, len, "%d", (int) s.st_ctime);
00310 break;
00311 case 'm':
00312 snprintf(buf, len, "%o", (int) s.st_mode);
00313 break;
00314 }
00315 }
00316
00317 return 0;
00318 }
00319
00320 enum file_format {
00321 FF_UNKNOWN = -1,
00322 FF_UNIX,
00323 FF_DOS,
00324 FF_MAC,
00325 };
00326
00327 static int64_t count_lines(const char *filename, enum file_format newline_format)
00328 {
00329 int count = 0;
00330 char fbuf[4096];
00331 FILE *ff;
00332
00333 if (!(ff = fopen(filename, "r"))) {
00334 ast_log(LOG_ERROR, "Unable to open '%s': %s\n", filename, strerror(errno));
00335 return -1;
00336 }
00337
00338 while (fgets(fbuf, sizeof(fbuf), ff)) {
00339 char *next = fbuf, *first_cr = NULL, *first_nl = NULL;
00340
00341
00342
00343 while (next) {
00344 if (newline_format == FF_DOS || newline_format == FF_MAC || newline_format == FF_UNKNOWN) {
00345 first_cr = strchr(next, '\r');
00346 }
00347 if (newline_format == FF_UNIX || newline_format == FF_UNKNOWN) {
00348 first_nl = strchr(next, '\n');
00349 }
00350
00351
00352 if (!first_cr && !first_nl) {
00353 break;
00354 }
00355
00356 if (newline_format == FF_UNKNOWN) {
00357 if ((first_cr && !first_nl) || (first_cr && first_cr < first_nl)) {
00358 if (first_nl && first_nl == first_cr + 1) {
00359 newline_format = FF_DOS;
00360 } else if (first_cr && first_cr == &fbuf[sizeof(fbuf) - 2]) {
00361
00362 fseek(ff, -1, SEEK_CUR);
00363 break;
00364 } else {
00365 newline_format = FF_MAC;
00366 first_nl = NULL;
00367 }
00368 } else {
00369 newline_format = FF_UNIX;
00370 first_cr = NULL;
00371 }
00372
00373 }
00374
00375 if (newline_format == FF_DOS) {
00376 if (first_nl && first_cr && first_nl == first_cr + 1) {
00377 next = first_nl + 1;
00378 count++;
00379 } else if (first_cr == &fbuf[sizeof(fbuf) - 2]) {
00380
00381 fseek(ff, -1, SEEK_CUR);
00382 break;
00383 }
00384 } else if (newline_format == FF_MAC) {
00385 if (first_cr) {
00386 next = first_cr + 1;
00387 count++;
00388 }
00389 } else if (newline_format == FF_UNIX) {
00390 if (first_nl) {
00391 next = first_nl + 1;
00392 count++;
00393 }
00394 }
00395 }
00396 }
00397 fclose(ff);
00398
00399 return count;
00400 }
00401
00402 static int file_count_line(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
00403 {
00404 enum file_format newline_format = FF_UNKNOWN;
00405 int64_t count;
00406 AST_DECLARE_APP_ARGS(args,
00407 AST_APP_ARG(filename);
00408 AST_APP_ARG(format);
00409 );
00410
00411 AST_STANDARD_APP_ARGS(args, data);
00412 if (args.argc > 1) {
00413 if (tolower(args.format[0]) == 'd') {
00414 newline_format = FF_DOS;
00415 } else if (tolower(args.format[0]) == 'm') {
00416 newline_format = FF_MAC;
00417 } else if (tolower(args.format[0]) == 'u') {
00418 newline_format = FF_UNIX;
00419 }
00420 }
00421
00422 count = count_lines(args.filename, newline_format);
00423 ast_str_set(buf, len, "%" PRId64, count);
00424 return 0;
00425 }
00426
00427 #define LINE_COUNTER(cptr, term, counter) \
00428 if (*cptr == '\n' && term == FF_UNIX) { \
00429 counter++; \
00430 } else if (*cptr == '\n' && term == FF_DOS && dos_state == 0) { \
00431 dos_state = 1; \
00432 } else if (*cptr == '\r' && term == FF_DOS && dos_state == 1) { \
00433 dos_state = 0; \
00434 counter++; \
00435 } else if (*cptr == '\r' && term == FF_MAC) { \
00436 counter++; \
00437 } else if (term == FF_DOS) { \
00438 dos_state = 0; \
00439 }
00440
00441 static enum file_format file2format(const char *filename)
00442 {
00443 FILE *ff;
00444 char fbuf[4096];
00445 char *first_cr, *first_nl;
00446 enum file_format newline_format = FF_UNKNOWN;
00447
00448 if (!(ff = fopen(filename, "r"))) {
00449 ast_log(LOG_ERROR, "Cannot open '%s': %s\n", filename, strerror(errno));
00450 return -1;
00451 }
00452
00453 while (fgets(fbuf, sizeof(fbuf), ff)) {
00454 first_cr = strchr(fbuf, '\r');
00455 first_nl = strchr(fbuf, '\n');
00456
00457 if (!first_cr && !first_nl) {
00458 continue;
00459 }
00460
00461 if ((first_cr && !first_nl) || (first_cr && first_cr < first_nl)) {
00462
00463 if (first_nl && first_nl == first_cr + 1) {
00464 newline_format = FF_DOS;
00465 } else if (first_cr && first_cr == &fbuf[sizeof(fbuf) - 2]) {
00466
00467 fseek(ff, -1, SEEK_CUR);
00468 continue;
00469 } else {
00470 newline_format = FF_MAC;
00471 }
00472 } else {
00473 newline_format = FF_UNIX;
00474 }
00475 break;
00476 }
00477 fclose(ff);
00478 return newline_format;
00479 }
00480
00481 static int file_format(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
00482 {
00483 enum file_format newline_format = file2format(data);
00484 ast_str_set(buf, len, "%c", newline_format == FF_UNIX ? 'u' : newline_format == FF_DOS ? 'd' : newline_format == FF_MAC ? 'm' : 'x');
00485 return 0;
00486 }
00487
00488 static int file_read(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
00489 {
00490 FILE *ff;
00491 int64_t offset = 0, length = LLONG_MAX;
00492 enum file_format format = FF_UNKNOWN;
00493 char fbuf[4096];
00494 int64_t flength, i;
00495 int64_t offset_offset = -1, length_offset = -1;
00496 char dos_state = 0;
00497 AST_DECLARE_APP_ARGS(args,
00498 AST_APP_ARG(filename);
00499 AST_APP_ARG(offset);
00500 AST_APP_ARG(length);
00501 AST_APP_ARG(options);
00502 AST_APP_ARG(fileformat);
00503 );
00504
00505 AST_STANDARD_APP_ARGS(args, data);
00506
00507 if (args.argc > 1) {
00508 sscanf(args.offset, "%" SCNd64, &offset);
00509 }
00510 if (args.argc > 2) {
00511 sscanf(args.length, "%" SCNd64, &length);
00512 }
00513
00514 if (args.argc < 4 || !strchr(args.options, 'l')) {
00515
00516 off_t off_i;
00517
00518 if (!(ff = fopen(args.filename, "r"))) {
00519 ast_log(LOG_WARNING, "Cannot open file '%s' for reading: %s\n", args.filename, strerror(errno));
00520 return 0;
00521 }
00522
00523 if (fseeko(ff, 0, SEEK_END) < 0) {
00524 ast_log(LOG_ERROR, "Cannot seek to end of '%s': %s\n", args.filename, strerror(errno));
00525 fclose(ff);
00526 return -1;
00527 }
00528 flength = ftello(ff);
00529
00530 if (offset < 0) {
00531 fseeko(ff, offset, SEEK_END);
00532 if ((offset = ftello(ff)) < 0) {
00533 ast_log(AST_LOG_ERROR, "Cannot determine offset position of '%s': %s\n", args.filename, strerror(errno));
00534 fclose(ff);
00535 return -1;
00536 }
00537 }
00538 if (length < 0) {
00539 fseeko(ff, length, SEEK_END);
00540 if ((length = ftello(ff)) - offset < 0) {
00541
00542 fclose(ff);
00543 return -1;
00544 }
00545 } else if (length == LLONG_MAX) {
00546 fseeko(ff, 0, SEEK_END);
00547 length = ftello(ff);
00548 }
00549
00550 ast_str_reset(*buf);
00551
00552 fseeko(ff, offset, SEEK_SET);
00553 for (off_i = ftello(ff); off_i < flength && off_i < offset + length; off_i += sizeof(fbuf)) {
00554
00555 size_t toappend = sizeof(fbuf);
00556
00557 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
00558 ast_log(LOG_ERROR, "Short read?!!\n");
00559 break;
00560 }
00561
00562
00563 if (off_i + toappend > offset + length) {
00564 toappend = length - off_i;
00565 }
00566
00567 ast_str_append_substr(buf, len, fbuf, toappend);
00568 }
00569 fclose(ff);
00570 return 0;
00571 }
00572
00573
00574 if (args.argc == 5) {
00575 if (tolower(args.fileformat[0]) == 'd') {
00576 format = FF_DOS;
00577 } else if (tolower(args.fileformat[0]) == 'm') {
00578 format = FF_MAC;
00579 } else if (tolower(args.fileformat[0]) == 'u') {
00580 format = FF_UNIX;
00581 }
00582 }
00583
00584 if (format == FF_UNKNOWN) {
00585 if ((format = file2format(args.filename)) == FF_UNKNOWN) {
00586 ast_log(LOG_WARNING, "'%s' is not a line-based file\n", args.filename);
00587 return -1;
00588 }
00589 }
00590
00591 if (offset < 0 && length <= offset) {
00592
00593 return -1;
00594 } else if (offset == 0) {
00595 offset_offset = 0;
00596 }
00597
00598 if (!(ff = fopen(args.filename, "r"))) {
00599 ast_log(LOG_ERROR, "Cannot open '%s': %s\n", args.filename, strerror(errno));
00600 return -1;
00601 }
00602
00603 if (fseek(ff, 0, SEEK_END)) {
00604 ast_log(LOG_ERROR, "Cannot seek to end of file '%s': %s\n", args.filename, strerror(errno));
00605 fclose(ff);
00606 return -1;
00607 }
00608
00609 flength = ftello(ff);
00610
00611 if (length == LLONG_MAX) {
00612 length_offset = flength;
00613 }
00614
00615
00616 if (offset < 0 || length < 0) {
00617 int64_t count = 0;
00618
00619
00620 for (i = (flength / sizeof(fbuf)) * sizeof(fbuf); i >= 0; i -= sizeof(fbuf)) {
00621 size_t end;
00622 char *pos;
00623 if (fseeko(ff, i, SEEK_SET)) {
00624 ast_log(LOG_ERROR, "Cannot seek to offset %" PRId64 ": %s\n", i, strerror(errno));
00625 }
00626 end = fread(fbuf, 1, sizeof(fbuf), ff);
00627 for (pos = (end < sizeof(fbuf) ? fbuf + end - 1 : fbuf + sizeof(fbuf) - 1); pos > fbuf - 1; pos--) {
00628 LINE_COUNTER(pos, format, count);
00629
00630 if (length < 0 && count * -1 == length) {
00631 length_offset = i + (pos - fbuf);
00632 } else if (offset < 0 && count * -1 == (offset - 1)) {
00633
00634 if (format == FF_DOS) {
00635 offset_offset = i + (pos - fbuf) + 2;
00636 } else {
00637 offset_offset = i + (pos - fbuf) + 1;
00638 }
00639 break;
00640 }
00641 }
00642 if ((offset < 0 && offset_offset >= 0) || (offset >= 0 && length_offset >= 0)) {
00643 break;
00644 }
00645 }
00646
00647 if (offset < 0 && offset_offset < 0 && offset == count * -1) {
00648 offset_offset = 0;
00649 }
00650 }
00651
00652
00653 if (offset > 0) {
00654 int64_t count = 0;
00655 fseek(ff, 0, SEEK_SET);
00656 for (i = 0; i < flength; i += sizeof(fbuf)) {
00657 char *pos;
00658 if (i + sizeof(fbuf) <= flength) {
00659
00660 memset(fbuf, 0, sizeof(fbuf));
00661 }
00662 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
00663 ast_log(LOG_ERROR, "Short read?!!\n");
00664 fclose(ff);
00665 return -1;
00666 }
00667 for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) {
00668 LINE_COUNTER(pos, format, count);
00669
00670 if (count == offset) {
00671 offset_offset = i + (pos - fbuf) + 1;
00672 break;
00673 }
00674 }
00675 if (offset_offset >= 0) {
00676 break;
00677 }
00678 }
00679 }
00680
00681 if (offset_offset < 0) {
00682 ast_log(LOG_ERROR, "Offset '%s' refers to before the beginning of the file!\n", args.offset);
00683 fclose(ff);
00684 return -1;
00685 }
00686
00687 ast_str_reset(*buf);
00688 if (fseeko(ff, offset_offset, SEEK_SET)) {
00689 ast_log(LOG_ERROR, "fseeko failed: %s\n", strerror(errno));
00690 }
00691
00692
00693
00694
00695
00696 if (length_offset >= 0) {
00697 ast_debug(3, "offset=%" PRId64 ", length=%" PRId64 ", offset_offset=%" PRId64 ", length_offset=%" PRId64 "\n", offset, length, offset_offset, length_offset);
00698 for (i = offset_offset; i < length_offset; i += sizeof(fbuf)) {
00699 if (fread(fbuf, 1, i + sizeof(fbuf) > flength ? flength - i : sizeof(fbuf), ff) < (i + sizeof(fbuf) > flength ? flength - i : sizeof(fbuf))) {
00700 ast_log(LOG_ERROR, "Short read?!!\n");
00701 }
00702 ast_debug(3, "Appending first %" PRId64" bytes of fbuf=%s\n", i + sizeof(fbuf) > length_offset ? length_offset - i : sizeof(fbuf), fbuf);
00703 ast_str_append_substr(buf, len, fbuf, i + sizeof(fbuf) > length_offset ? length_offset - i : sizeof(fbuf));
00704 }
00705 } else if (length == 0) {
00706
00707 } else {
00708
00709 int64_t current_length = 0;
00710 char dos_state = 0;
00711 ast_debug(3, "offset=%" PRId64 ", length=%" PRId64 ", offset_offset=%" PRId64 ", length_offset=%" PRId64 "\n", offset, length, offset_offset, length_offset);
00712 for (i = offset_offset; i < flength; i += sizeof(fbuf)) {
00713 char *pos;
00714 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
00715 ast_log(LOG_ERROR, "Short read?!!\n");
00716 fclose(ff);
00717 return -1;
00718 }
00719 for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) {
00720 LINE_COUNTER(pos, format, current_length);
00721
00722 if (current_length == length) {
00723 length_offset = i + (pos - fbuf) + 1;
00724 break;
00725 }
00726 }
00727 ast_debug(3, "length_offset=%" PRId64 ", length_offset - i=%" PRId64 "\n", length_offset, length_offset - i);
00728 ast_str_append_substr(buf, len, fbuf, length_offset >= 0 ? length_offset - i : flength > i + sizeof(fbuf)) ? sizeof(fbuf) : flength - i;
00729
00730 if (length_offset >= 0) {
00731 break;
00732 }
00733 }
00734 }
00735
00736 fclose(ff);
00737 return 0;
00738 }
00739
00740 const char *format2term(enum file_format f) __attribute__((const));
00741 const char *format2term(enum file_format f)
00742 {
00743 const char *term[] = { "", "\n", "\r\n", "\r" };
00744 return term[f + 1];
00745 }
00746
00747 static int file_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00748 {
00749 AST_DECLARE_APP_ARGS(args,
00750 AST_APP_ARG(filename);
00751 AST_APP_ARG(offset);
00752 AST_APP_ARG(length);
00753 AST_APP_ARG(options);
00754 AST_APP_ARG(format);
00755 );
00756 int64_t offset = 0, length = LLONG_MAX;
00757 off_t flength, vlength;
00758 size_t foplen = 0;
00759 FILE *ff;
00760
00761 AST_STANDARD_APP_ARGS(args, data);
00762
00763 if (args.argc > 1) {
00764 sscanf(args.offset, "%" SCNd64, &offset);
00765 }
00766 if (args.argc > 2) {
00767 sscanf(args.length, "%" SCNd64, &length);
00768 }
00769
00770 vlength = strlen(value);
00771
00772 if (args.argc < 4 || !strchr(args.options, 'l')) {
00773
00774
00775 if (args.argc > 3 && strchr(args.options, 'a')) {
00776
00777 if (!(ff = fopen(args.filename, "a"))) {
00778 ast_log(LOG_WARNING, "Cannot open file '%s' for appending: %s\n", args.filename, strerror(errno));
00779 return 0;
00780 }
00781 if (fwrite(value, 1, vlength, ff) < vlength) {
00782 ast_log(LOG_ERROR, "Short write?!!\n");
00783 }
00784 fclose(ff);
00785 return 0;
00786 } else if (offset == 0 && length == LLONG_MAX) {
00787 if (!(ff = fopen(args.filename, "w"))) {
00788 ast_log(LOG_WARNING, "Cannot open file '%s' for writing: %s\n", args.filename, strerror(errno));
00789 return 0;
00790 }
00791 if (fwrite(value, 1, vlength, ff) < vlength) {
00792 ast_log(LOG_ERROR, "Short write?!!\n");
00793 }
00794 fclose(ff);
00795 return 0;
00796 }
00797
00798 if (!(ff = fopen(args.filename, "r+"))) {
00799 ast_log(LOG_WARNING, "Cannot open file '%s' for modification: %s\n", args.filename, strerror(errno));
00800 return 0;
00801 }
00802 fseeko(ff, 0, SEEK_END);
00803 flength = ftello(ff);
00804
00805 if (offset < 0) {
00806 if (fseeko(ff, offset, SEEK_END)) {
00807 ast_log(LOG_ERROR, "Cannot seek to offset of '%s': %s\n", args.filename, strerror(errno));
00808 fclose(ff);
00809 return -1;
00810 }
00811 if ((offset = ftello(ff)) < 0) {
00812 ast_log(AST_LOG_ERROR, "Cannot determine offset position of '%s': %s\n", args.filename, strerror(errno));
00813 fclose(ff);
00814 return -1;
00815 }
00816 }
00817
00818 if (length < 0) {
00819 length = flength - offset + length;
00820 if (length < 0) {
00821 ast_log(LOG_ERROR, "Length '%s' exceeds the file length. No data will be written.\n", args.length);
00822 fclose(ff);
00823 return -1;
00824 }
00825 }
00826
00827 fseeko(ff, offset, SEEK_SET);
00828
00829 ast_debug(3, "offset=%s/%" PRId64 ", length=%s/%" PRId64 ", vlength=%" PRId64 ", flength=%" PRId64 "\n",
00830 S_OR(args.offset, "(null)"), offset, S_OR(args.length, "(null)"), length, vlength, flength);
00831
00832 if (length == vlength) {
00833
00834 if (fwrite(value, 1, vlength, ff) < vlength) {
00835 ast_log(LOG_ERROR, "Short write?!!\n");
00836 }
00837 fclose(ff);
00838 } else if (length == LLONG_MAX) {
00839
00840 if (fwrite(value, 1, vlength, ff) < vlength) {
00841 ast_log(LOG_ERROR, "Short write?!!\n");
00842 }
00843 fclose(ff);
00844 if (truncate(args.filename, offset + vlength)) {
00845 ast_log(LOG_ERROR, "Unable to truncate the file: %s\n", strerror(errno));
00846 }
00847 } else if (length > vlength) {
00848
00849 char fbuf[4096];
00850 off_t cur;
00851 if (fwrite(value, 1, vlength, ff) < vlength) {
00852 ast_log(LOG_ERROR, "Short write?!!\n");
00853 }
00854 fseeko(ff, length - vlength, SEEK_CUR);
00855 while ((cur = ftello(ff)) < flength) {
00856 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
00857 ast_log(LOG_ERROR, "Short read?!!\n");
00858 }
00859 fseeko(ff, cur + vlength - length, SEEK_SET);
00860 if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
00861 ast_log(LOG_ERROR, "Short write?!!\n");
00862 }
00863
00864 if (fseeko(ff, cur + sizeof(fbuf), SEEK_SET) < 0) {
00865
00866 break;
00867 }
00868 }
00869 fclose(ff);
00870 if (truncate(args.filename, flength - (length - vlength))) {
00871 ast_log(LOG_ERROR, "Unable to truncate the file: %s\n", strerror(errno));
00872 }
00873 } else {
00874
00875 char fbuf[4096];
00876 off_t lastwritten = flength + vlength - length;
00877
00878
00879 fseeko(ff, flength - sizeof(fbuf), SEEK_SET);
00880 while (offset < ftello(ff)) {
00881 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
00882 ast_log(LOG_ERROR, "Short read?!!\n");
00883 fclose(ff);
00884 return -1;
00885 }
00886
00887
00888
00889 fseeko(ff, vlength - length - sizeof(fbuf), SEEK_CUR);
00890
00891
00892 lastwritten = ftello(ff);
00893
00894 if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
00895 ast_log(LOG_ERROR, "Short write?!!\n");
00896 fclose(ff);
00897 return -1;
00898 }
00899
00900 if (lastwritten < offset + sizeof(fbuf)) {
00901 break;
00902 }
00903
00904
00905
00906
00907
00908
00909 fseeko(ff, 2 * sizeof(fbuf) + vlength - length, SEEK_CUR);
00910 }
00911
00912
00913 if (fseeko(ff, offset + length, SEEK_SET)) {
00914 ast_log(LOG_WARNING, "Unable to seek to %" PRId64 " + %" PRId64 " != %" PRId64 "?)\n", offset, length, ftello(ff));
00915 }
00916
00917
00918 ast_debug(1, "Reading at %" PRId64 "\n", ftello(ff));
00919 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
00920 ast_log(LOG_ERROR, "Short read?!!\n");
00921 }
00922 fseek(ff, offset, SEEK_SET);
00923
00924 if (fwrite(value, 1, vlength, ff) < vlength) {
00925 ast_log(LOG_ERROR, "Short write?!!\n");
00926 } else {
00927 off_t curpos = ftello(ff);
00928 foplen = lastwritten - curpos;
00929 if (fwrite(fbuf, 1, foplen, ff) < foplen) {
00930 ast_log(LOG_ERROR, "Short write?!!\n");
00931 }
00932 }
00933 fclose(ff);
00934 }
00935 } else {
00936 enum file_format newline_format = FF_UNKNOWN;
00937
00938
00939 if (args.argc == 5) {
00940 if (tolower(args.format[0]) == 'u') {
00941 newline_format = FF_UNIX;
00942 } else if (tolower(args.format[0]) == 'm') {
00943 newline_format = FF_MAC;
00944 } else if (tolower(args.format[0]) == 'd') {
00945 newline_format = FF_DOS;
00946 }
00947 }
00948 if (newline_format == FF_UNKNOWN && (newline_format = file2format(args.filename)) == FF_UNKNOWN) {
00949 ast_log(LOG_ERROR, "File '%s' not in line format\n", args.filename);
00950 return -1;
00951 }
00952
00953 if (strchr(args.options, 'a')) {
00954
00955 if (!(ff = fopen(args.filename, "a"))) {
00956 ast_log(LOG_ERROR, "Unable to open '%s' for appending: %s\n", args.filename, strerror(errno));
00957 return -1;
00958 }
00959 if (fwrite(value, 1, vlength, ff) < vlength) {
00960 ast_log(LOG_ERROR, "Short write?!!\n");
00961 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) {
00962 ast_log(LOG_ERROR, "Short write?!!\n");
00963 }
00964 fclose(ff);
00965 } else if (offset == 0 && length == LLONG_MAX) {
00966
00967 off_t truncsize;
00968 if (!(ff = fopen(args.filename, "w"))) {
00969 ast_log(LOG_ERROR, "Unable to open '%s' for writing: %s\n", args.filename, strerror(errno));
00970 return -1;
00971 }
00972 if (fwrite(value, 1, vlength, ff) < vlength) {
00973 ast_log(LOG_ERROR, "Short write?!!\n");
00974 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) {
00975 ast_log(LOG_ERROR, "Short write?!!\n");
00976 }
00977 if ((truncsize = ftello(ff)) < 0) {
00978 ast_log(AST_LOG_ERROR, "Unable to determine truncate position of '%s': %s\n", args.filename, strerror(errno));
00979 }
00980 fclose(ff);
00981 if (truncsize >= 0 && truncate(args.filename, truncsize)) {
00982 ast_log(LOG_ERROR, "Unable to truncate file '%s': %s\n", args.filename, strerror(errno));
00983 return -1;
00984 }
00985 } else {
00986 int64_t offset_offset = (offset == 0 ? 0 : -1), length_offset = -1, flength, i, current_length = 0;
00987 char dos_state = 0, fbuf[4096];
00988
00989 if (offset < 0 && length < offset) {
00990
00991 ast_log(LOG_ERROR, "Length cannot specify a position prior to the offset\n");
00992 return -1;
00993 }
00994
00995 if (!(ff = fopen(args.filename, "r+"))) {
00996 ast_log(LOG_ERROR, "Cannot open '%s' for modification: %s\n", args.filename, strerror(errno));
00997 return -1;
00998 }
00999
01000 if (fseek(ff, 0, SEEK_END)) {
01001 ast_log(LOG_ERROR, "Cannot seek to end of file '%s': %s\n", args.filename, strerror(errno));
01002 fclose(ff);
01003 return -1;
01004 }
01005 if ((flength = ftello(ff)) < 0) {
01006 ast_log(AST_LOG_ERROR, "Cannot determine end position of file '%s': %s\n", args.filename, strerror(errno));
01007 fclose(ff);
01008 return -1;
01009 }
01010
01011
01012 if (offset < 0 || length < 0) {
01013 int64_t count = 0;
01014 for (i = (flength / sizeof(fbuf)) * sizeof(fbuf); i >= 0; i -= sizeof(fbuf)) {
01015 char *pos;
01016 if (fseeko(ff, i, SEEK_SET)) {
01017 ast_log(LOG_ERROR, "Cannot seek to offset %" PRId64 ": %s\n", i, strerror(errno));
01018 }
01019 if (i + sizeof(fbuf) >= flength) {
01020 memset(fbuf, 0, sizeof(fbuf));
01021 }
01022 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
01023 ast_log(LOG_ERROR, "Short read: %s\n", strerror(errno));
01024 fclose(ff);
01025 return -1;
01026 }
01027 for (pos = fbuf + sizeof(fbuf) - 1; pos > fbuf - 1; pos--) {
01028 LINE_COUNTER(pos, newline_format, count);
01029
01030 if (length < 0 && count * -1 == length) {
01031 length_offset = i + (pos - fbuf);
01032 } else if (offset < 0 && count * -1 == (offset - 1)) {
01033
01034 if (newline_format == FF_DOS) {
01035 offset_offset = i + (pos - fbuf) + 2;
01036 } else {
01037 offset_offset = i + (pos - fbuf) + 1;
01038 }
01039 break;
01040 }
01041 }
01042 if ((offset < 0 && offset_offset >= 0) || (offset >= 0 && length_offset >= 0)) {
01043 break;
01044 }
01045 }
01046
01047 if (offset < 0 && offset_offset < 0 && offset == count * -1) {
01048 offset_offset = 0;
01049 }
01050 }
01051
01052
01053 if (offset > 0) {
01054 int64_t count = 0;
01055 fseek(ff, 0, SEEK_SET);
01056 for (i = 0; i < flength; i += sizeof(fbuf)) {
01057 char *pos;
01058 if (i + sizeof(fbuf) >= flength) {
01059 memset(fbuf, 0, sizeof(fbuf));
01060 }
01061 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
01062 ast_log(LOG_ERROR, "Short read?!!\n");
01063 fclose(ff);
01064 return -1;
01065 }
01066 for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) {
01067 LINE_COUNTER(pos, newline_format, count);
01068
01069 if (count == offset) {
01070 offset_offset = i + (pos - fbuf) + 1;
01071 break;
01072 }
01073 }
01074 if (offset_offset >= 0) {
01075 break;
01076 }
01077 }
01078 }
01079
01080 if (offset_offset < 0) {
01081 ast_log(LOG_ERROR, "Offset '%s' refers to before the beginning of the file!\n", args.offset);
01082 fclose(ff);
01083 return -1;
01084 }
01085
01086 if (length == 0) {
01087 length_offset = offset_offset;
01088 } else if (length == LLONG_MAX) {
01089 length_offset = flength;
01090 }
01091
01092
01093 if (length_offset < 0) {
01094 fseeko(ff, offset_offset, SEEK_SET);
01095 for (i = offset_offset; i < flength; i += sizeof(fbuf)) {
01096 char *pos;
01097 if (i + sizeof(fbuf) >= flength) {
01098 memset(fbuf, 0, sizeof(fbuf));
01099 }
01100 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
01101 ast_log(LOG_ERROR, "Short read?!!\n");
01102 fclose(ff);
01103 return -1;
01104 }
01105 for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) {
01106 LINE_COUNTER(pos, newline_format, current_length);
01107
01108 if (current_length == length) {
01109 length_offset = i + (pos - fbuf) + 1;
01110 break;
01111 }
01112 }
01113 if (length_offset >= 0) {
01114 break;
01115 }
01116 }
01117 if (length_offset < 0) {
01118
01119 ast_debug(3, "Exceeds length of file? length=%" PRId64 ", count=%" PRId64 ", flength=%" PRId64 "\n", length, current_length, flength);
01120 length_offset = flength;
01121 }
01122 }
01123
01124
01125 if (length_offset - offset_offset == vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format)))) {
01126
01127 fseeko(ff, offset_offset, SEEK_SET);
01128 if (fwrite(value, 1, vlength, ff) < vlength) {
01129 ast_log(LOG_ERROR, "Short write?!!\n");
01130 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) {
01131 ast_log(LOG_ERROR, "Short write?!!\n");
01132 }
01133 fclose(ff);
01134 } else if (length_offset - offset_offset > vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format)))) {
01135
01136 off_t cur;
01137 int64_t length_length = length_offset - offset_offset;
01138 size_t vlen = vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format)));
01139
01140 ast_debug(3, "offset=%s/%" PRId64 ", length=%s/%" PRId64 " (%" PRId64 "), vlength=%" PRId64 ", flength=%" PRId64 "\n",
01141 args.offset, offset_offset, args.length, length_offset, length_length, vlength, flength);
01142
01143 fseeko(ff, offset_offset, SEEK_SET);
01144 if (fwrite(value, 1, vlength, ff) < vlength) {
01145 ast_log(LOG_ERROR, "Short write?!!\n");
01146 fclose(ff);
01147 return -1;
01148 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, vlen - vlength, ff) < vlen - vlength) {
01149 ast_log(LOG_ERROR, "Short write?!!\n");
01150 fclose(ff);
01151 return -1;
01152 }
01153 while ((cur = ftello(ff)) < flength) {
01154 if (cur < 0) {
01155 ast_log(AST_LOG_ERROR, "Unable to determine last write position for '%s': %s\n", args.filename, strerror(errno));
01156 fclose(ff);
01157 return -1;
01158 }
01159 fseeko(ff, length_length - vlen, SEEK_CUR);
01160 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
01161 ast_log(LOG_ERROR, "Short read?!!\n");
01162 fclose(ff);
01163 return -1;
01164 }
01165
01166 fseeko(ff, cur, SEEK_SET);
01167 if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
01168 ast_log(LOG_ERROR, "Short write?!!\n");
01169 fclose(ff);
01170 return -1;
01171 }
01172 }
01173 fclose(ff);
01174 if (truncate(args.filename, flength - (length_length - vlen))) {
01175 ast_log(LOG_ERROR, "Truncation of file failed: %s\n", strerror(errno));
01176 }
01177 } else {
01178
01179 size_t vlen = vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format)));
01180 int64_t origlen = length_offset - offset_offset;
01181 off_t lastwritten = flength + vlen - origlen;
01182
01183 ast_debug(3, "offset=%s/%" PRId64 ", length=%s/%" PRId64 ", vlength=%" PRId64 ", flength=%" PRId64 "\n",
01184 args.offset, offset_offset, args.length, length_offset, vlength, flength);
01185
01186 fseeko(ff, flength - sizeof(fbuf), SEEK_SET);
01187 while (offset_offset + sizeof(fbuf) < ftello(ff)) {
01188 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
01189 ast_log(LOG_ERROR, "Short read?!!\n");
01190 fclose(ff);
01191 return -1;
01192 }
01193 fseeko(ff, sizeof(fbuf) - vlen - origlen, SEEK_CUR);
01194 if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
01195 ast_log(LOG_ERROR, "Short write?!!\n");
01196 fclose(ff);
01197 return -1;
01198 }
01199 if ((lastwritten = ftello(ff) - sizeof(fbuf)) < offset_offset + sizeof(fbuf)) {
01200 break;
01201 }
01202 fseeko(ff, 2 * sizeof(fbuf) + vlen - origlen, SEEK_CUR);
01203 }
01204 fseek(ff, length_offset, SEEK_SET);
01205 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
01206 ast_log(LOG_ERROR, "Short read?!!\n");
01207 fclose(ff);
01208 return -1;
01209 }
01210 fseek(ff, offset_offset, SEEK_SET);
01211 if (fwrite(value, 1, vlength, ff) < vlength) {
01212 ast_log(LOG_ERROR, "Short write?!!\n");
01213 fclose(ff);
01214 return -1;
01215 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) {
01216 ast_log(LOG_ERROR, "Short write?!!\n");
01217 fclose(ff);
01218 return -1;
01219 } else {
01220 off_t curpos = ftello(ff);
01221 foplen = lastwritten - curpos;
01222 if (fwrite(fbuf, 1, foplen, ff) < foplen) {
01223 ast_log(LOG_ERROR, "Short write?!!\n");
01224 }
01225 }
01226 fclose(ff);
01227 }
01228 }
01229 }
01230
01231 return 0;
01232 }
01233
01234 static struct ast_custom_function env_function = {
01235 .name = "ENV",
01236 .read = env_read,
01237 .write = env_write
01238 };
01239
01240 static struct ast_custom_function stat_function = {
01241 .name = "STAT",
01242 .read = stat_read,
01243 .read_max = 12,
01244 };
01245
01246 static struct ast_custom_function file_function = {
01247 .name = "FILE",
01248 .read2 = file_read,
01249 .write = file_write,
01250 };
01251
01252 static struct ast_custom_function file_count_line_function = {
01253 .name = "FILE_COUNT_LINE",
01254 .read2 = file_count_line,
01255 .read_max = 12,
01256 };
01257
01258 static struct ast_custom_function file_format_function = {
01259 .name = "FILE_FORMAT",
01260 .read2 = file_format,
01261 .read_max = 2,
01262 };
01263
01264 static int unload_module(void)
01265 {
01266 int res = 0;
01267
01268 res |= ast_custom_function_unregister(&env_function);
01269 res |= ast_custom_function_unregister(&stat_function);
01270 res |= ast_custom_function_unregister(&file_function);
01271 res |= ast_custom_function_unregister(&file_count_line_function);
01272 res |= ast_custom_function_unregister(&file_format_function);
01273
01274 return res;
01275 }
01276
01277 static int load_module(void)
01278 {
01279 int res = 0;
01280
01281 res |= ast_custom_function_register(&env_function);
01282 res |= ast_custom_function_register_escalating(&stat_function, AST_CFE_READ);
01283 res |= ast_custom_function_register_escalating(&file_function, AST_CFE_BOTH);
01284 res |= ast_custom_function_register_escalating(&file_count_line_function, AST_CFE_READ);
01285 res |= ast_custom_function_register_escalating(&file_format_function, AST_CFE_READ);
01286
01287 return res;
01288 }
01289
01290 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Environment/filesystem dialplan functions");