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: 413592 $")
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, "%u", (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", 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 size_t readlen;
00498 AST_DECLARE_APP_ARGS(args,
00499 AST_APP_ARG(filename);
00500 AST_APP_ARG(offset);
00501 AST_APP_ARG(length);
00502 AST_APP_ARG(options);
00503 AST_APP_ARG(fileformat);
00504 );
00505
00506 AST_STANDARD_APP_ARGS(args, data);
00507
00508 if (args.argc > 1) {
00509 sscanf(args.offset, "%" SCNd64, &offset);
00510 }
00511 if (args.argc > 2) {
00512 sscanf(args.length, "%" SCNd64, &length);
00513 }
00514
00515 if (args.argc < 4 || !strchr(args.options, 'l')) {
00516
00517 off_t off_i;
00518
00519 if (!(ff = fopen(args.filename, "r"))) {
00520 ast_log(LOG_WARNING, "Cannot open file '%s' for reading: %s\n", args.filename, strerror(errno));
00521 return 0;
00522 }
00523
00524 if (fseeko(ff, 0, SEEK_END) < 0) {
00525 ast_log(LOG_ERROR, "Cannot seek to end of '%s': %s\n", args.filename, strerror(errno));
00526 fclose(ff);
00527 return -1;
00528 }
00529 flength = ftello(ff);
00530
00531 if (offset < 0) {
00532 fseeko(ff, offset, SEEK_END);
00533 if ((offset = ftello(ff)) < 0) {
00534 ast_log(AST_LOG_ERROR, "Cannot determine offset position of '%s': %s\n", args.filename, strerror(errno));
00535 fclose(ff);
00536 return -1;
00537 }
00538 }
00539 if (length < 0) {
00540 fseeko(ff, length, SEEK_END);
00541 if ((length = ftello(ff)) - offset < 0) {
00542
00543 fclose(ff);
00544 return -1;
00545 }
00546 } else if (length == LLONG_MAX) {
00547 fseeko(ff, 0, SEEK_END);
00548 length = ftello(ff);
00549 }
00550
00551 ast_str_reset(*buf);
00552
00553 fseeko(ff, offset, SEEK_SET);
00554 for (off_i = ftello(ff); off_i < flength && off_i < offset + length; off_i += sizeof(fbuf)) {
00555
00556 size_t toappend = sizeof(fbuf);
00557
00558 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
00559 ast_log(LOG_ERROR, "Short read?!!\n");
00560 break;
00561 }
00562
00563
00564 if (off_i + toappend > offset + length) {
00565 toappend = length - off_i;
00566 }
00567
00568 ast_str_append_substr(buf, len, fbuf, toappend);
00569 }
00570 fclose(ff);
00571 return 0;
00572 }
00573
00574
00575 if (args.argc == 5) {
00576 if (tolower(args.fileformat[0]) == 'd') {
00577 format = FF_DOS;
00578 } else if (tolower(args.fileformat[0]) == 'm') {
00579 format = FF_MAC;
00580 } else if (tolower(args.fileformat[0]) == 'u') {
00581 format = FF_UNIX;
00582 }
00583 }
00584
00585 if (format == FF_UNKNOWN) {
00586 if ((format = file2format(args.filename)) == FF_UNKNOWN) {
00587 ast_log(LOG_WARNING, "'%s' is not a line-based file\n", args.filename);
00588 return -1;
00589 }
00590 }
00591
00592 if (offset < 0 && length <= offset) {
00593
00594 return -1;
00595 } else if (offset == 0) {
00596 offset_offset = 0;
00597 }
00598
00599 if (!(ff = fopen(args.filename, "r"))) {
00600 ast_log(LOG_ERROR, "Cannot open '%s': %s\n", args.filename, strerror(errno));
00601 return -1;
00602 }
00603
00604 if (fseek(ff, 0, SEEK_END)) {
00605 ast_log(LOG_ERROR, "Cannot seek to end of file '%s': %s\n", args.filename, strerror(errno));
00606 fclose(ff);
00607 return -1;
00608 }
00609
00610 flength = ftello(ff);
00611
00612 if (length == LLONG_MAX) {
00613 length_offset = flength;
00614 }
00615
00616
00617 if (offset < 0 || length < 0) {
00618 int64_t count = 0;
00619
00620
00621 for (i = (flength / sizeof(fbuf)) * sizeof(fbuf); i >= 0; i -= sizeof(fbuf)) {
00622 size_t end;
00623 char *pos;
00624 if (fseeko(ff, i, SEEK_SET)) {
00625 ast_log(LOG_ERROR, "Cannot seek to offset %" PRId64 ": %s\n", i, strerror(errno));
00626 }
00627 end = fread(fbuf, 1, sizeof(fbuf), ff);
00628 for (pos = end < sizeof(fbuf) ? fbuf + end - 1 : fbuf + sizeof(fbuf) - 1; pos > fbuf - 1; pos--) {
00629 LINE_COUNTER(pos, format, count);
00630
00631 if (length < 0 && count * -1 == length) {
00632 length_offset = i + (pos - fbuf);
00633 } else if (offset < 0 && count * -1 == (offset - 1)) {
00634
00635 if (format == FF_DOS) {
00636 offset_offset = i + (pos - fbuf) + 2;
00637 } else {
00638 offset_offset = i + (pos - fbuf) + 1;
00639 }
00640 break;
00641 }
00642 }
00643 if ((offset < 0 && offset_offset >= 0) || (offset >= 0 && length_offset >= 0)) {
00644 break;
00645 }
00646 }
00647
00648 if (offset < 0 && offset_offset < 0 && offset == count * -1) {
00649 offset_offset = 0;
00650 }
00651 }
00652
00653
00654 if (offset > 0) {
00655 int64_t count = 0;
00656 fseek(ff, 0, SEEK_SET);
00657 for (i = 0; i < flength; i += sizeof(fbuf)) {
00658 char *pos;
00659 if (i + sizeof(fbuf) <= flength) {
00660
00661 memset(fbuf, 0, sizeof(fbuf));
00662 }
00663 if (fread(fbuf, 1, sizeof(fbuf), ff) && !feof(ff)) {
00664 ast_log(LOG_ERROR, "Short read?!!\n");
00665 fclose(ff);
00666 return -1;
00667 }
00668 for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) {
00669 LINE_COUNTER(pos, format, count);
00670
00671 if (count == offset) {
00672 offset_offset = i + (pos - fbuf) + 1;
00673 break;
00674 }
00675 }
00676 if (offset_offset >= 0) {
00677 break;
00678 }
00679 }
00680 }
00681
00682 if (offset_offset < 0) {
00683 ast_log(LOG_ERROR, "Offset '%s' refers to before the beginning of the file!\n", args.offset);
00684 fclose(ff);
00685 return -1;
00686 }
00687
00688 ast_str_reset(*buf);
00689 if (fseeko(ff, offset_offset, SEEK_SET)) {
00690 ast_log(LOG_ERROR, "fseeko failed: %s\n", strerror(errno));
00691 }
00692
00693
00694
00695
00696
00697 if (length_offset >= 0) {
00698 ast_debug(3, "offset=%" PRId64 ", length=%" PRId64 ", offset_offset=%" PRId64 ", length_offset=%" PRId64 "\n", offset, length, offset_offset, length_offset);
00699 for (i = offset_offset; i < length_offset; i += sizeof(fbuf)) {
00700 if (fread(fbuf, 1, i + sizeof(fbuf) > flength ? flength - i : sizeof(fbuf), ff) < (i + sizeof(fbuf) > flength ? flength - i : sizeof(fbuf))) {
00701 ast_log(LOG_ERROR, "Short read?!!\n");
00702 }
00703 ast_debug(3, "Appending first %" PRId64" bytes of fbuf=%s\n", (int64_t)(i + sizeof(fbuf) > length_offset ? length_offset - i : sizeof(fbuf)), fbuf);
00704 ast_str_append_substr(buf, len, fbuf, i + sizeof(fbuf) > length_offset ? length_offset - i : sizeof(fbuf));
00705 }
00706 } else if (length == 0) {
00707
00708 } else {
00709
00710 int64_t current_length = 0;
00711 char dos_state = 0;
00712 ast_debug(3, "offset=%" PRId64 ", length=%" PRId64 ", offset_offset=%" PRId64 ", length_offset=%" PRId64 "\n", offset, length, offset_offset, length_offset);
00713 for (i = offset_offset; i < flength; i += sizeof(fbuf)) {
00714 char *pos;
00715 if ((readlen = fread(fbuf, 1, sizeof(fbuf), ff)) < sizeof(fbuf) && !feof(ff)) {
00716 ast_log(LOG_ERROR, "Short read?!!\n");
00717 }
00718 for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) {
00719 LINE_COUNTER(pos, format, current_length);
00720
00721 if (current_length == length) {
00722 length_offset = i + (pos - fbuf) + 1;
00723 break;
00724 }
00725 }
00726 ast_debug(3, "length_offset=%" PRId64 ", length_offset - i=%" PRId64 "\n", length_offset, length_offset - i);
00727 ast_str_append_substr(buf, len, fbuf, length_offset >= 0 ? length_offset - i : flength > i + sizeof(fbuf)) ? sizeof(fbuf) : flength - i;
00728
00729 if (length_offset >= 0) {
00730 break;
00731 }
00732 }
00733 }
00734
00735 fclose(ff);
00736 return 0;
00737 }
00738
00739 const char *format2term(enum file_format f) __attribute__((const));
00740 const char *format2term(enum file_format f)
00741 {
00742 const char *term[] = { "", "\n", "\r\n", "\r" };
00743 return term[f + 1];
00744 }
00745
00746 static int file_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00747 {
00748 AST_DECLARE_APP_ARGS(args,
00749 AST_APP_ARG(filename);
00750 AST_APP_ARG(offset);
00751 AST_APP_ARG(length);
00752 AST_APP_ARG(options);
00753 AST_APP_ARG(format);
00754 );
00755 int64_t offset = 0, length = LLONG_MAX;
00756 off_t flength, vlength;
00757 size_t foplen = 0;
00758 FILE *ff;
00759
00760 AST_STANDARD_APP_ARGS(args, data);
00761
00762 if (args.argc > 1) {
00763 sscanf(args.offset, "%" SCNd64, &offset);
00764 }
00765 if (args.argc > 2) {
00766 sscanf(args.length, "%" SCNd64, &length);
00767 }
00768
00769 vlength = strlen(value);
00770
00771 if (args.argc < 4 || !strchr(args.options, 'l')) {
00772
00773
00774 if (args.argc > 3 && strchr(args.options, 'a')) {
00775
00776 if (!(ff = fopen(args.filename, "a"))) {
00777 ast_log(LOG_WARNING, "Cannot open file '%s' for appending: %s\n", args.filename, strerror(errno));
00778 return 0;
00779 }
00780 if (fwrite(value, 1, vlength, ff) < vlength) {
00781 ast_log(LOG_ERROR, "Short write?!!\n");
00782 }
00783 fclose(ff);
00784 return 0;
00785 } else if (offset == 0 && length == LLONG_MAX) {
00786 if (!(ff = fopen(args.filename, "w"))) {
00787 ast_log(LOG_WARNING, "Cannot open file '%s' for writing: %s\n", args.filename, strerror(errno));
00788 return 0;
00789 }
00790 if (fwrite(value, 1, vlength, ff) < vlength) {
00791 ast_log(LOG_ERROR, "Short write?!!\n");
00792 }
00793 fclose(ff);
00794 return 0;
00795 }
00796
00797 if (!(ff = fopen(args.filename, "r+"))) {
00798 ast_log(LOG_WARNING, "Cannot open file '%s' for modification: %s\n", args.filename, strerror(errno));
00799 return 0;
00800 }
00801 fseeko(ff, 0, SEEK_END);
00802 flength = ftello(ff);
00803
00804 if (offset < 0) {
00805 if (fseeko(ff, offset, SEEK_END)) {
00806 ast_log(LOG_ERROR, "Cannot seek to offset of '%s': %s\n", args.filename, strerror(errno));
00807 fclose(ff);
00808 return -1;
00809 }
00810 if ((offset = ftello(ff)) < 0) {
00811 ast_log(AST_LOG_ERROR, "Cannot determine offset position of '%s': %s\n", args.filename, strerror(errno));
00812 fclose(ff);
00813 return -1;
00814 }
00815 }
00816
00817 if (length < 0) {
00818 length = flength - offset + length;
00819 if (length < 0) {
00820 ast_log(LOG_ERROR, "Length '%s' exceeds the file length. No data will be written.\n", args.length);
00821 fclose(ff);
00822 return -1;
00823 }
00824 }
00825
00826 fseeko(ff, offset, SEEK_SET);
00827
00828 ast_debug(3, "offset=%s/%" PRId64 ", length=%s/%" PRId64 ", vlength=%" PRId64 ", flength=%" PRId64 "\n",
00829 S_OR(args.offset, "(null)"), offset, S_OR(args.length, "(null)"), length, vlength, flength);
00830
00831 if (length == vlength) {
00832
00833 if (fwrite(value, 1, vlength, ff) < vlength) {
00834 ast_log(LOG_ERROR, "Short write?!!\n");
00835 }
00836 fclose(ff);
00837 } else if (length == LLONG_MAX) {
00838
00839 if (fwrite(value, 1, vlength, ff) < vlength) {
00840 ast_log(LOG_ERROR, "Short write?!!\n");
00841 }
00842 fclose(ff);
00843 if (truncate(args.filename, offset + vlength)) {
00844 ast_log(LOG_ERROR, "Unable to truncate the file: %s\n", strerror(errno));
00845 }
00846 } else if (length > vlength) {
00847
00848 char fbuf[4096];
00849 off_t cur;
00850 if (fwrite(value, 1, vlength, ff) < vlength) {
00851 ast_log(LOG_ERROR, "Short write?!!\n");
00852 }
00853 fseeko(ff, length - vlength, SEEK_CUR);
00854 while ((cur = ftello(ff)) < flength) {
00855 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
00856 ast_log(LOG_ERROR, "Short read?!!\n");
00857 }
00858 fseeko(ff, cur + vlength - length, SEEK_SET);
00859 if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
00860 ast_log(LOG_ERROR, "Short write?!!\n");
00861 }
00862
00863 if (fseeko(ff, cur + sizeof(fbuf), SEEK_SET) < 0) {
00864
00865 break;
00866 }
00867 }
00868 fclose(ff);
00869 if (truncate(args.filename, flength - (length - vlength))) {
00870 ast_log(LOG_ERROR, "Unable to truncate the file: %s\n", strerror(errno));
00871 }
00872 } else {
00873
00874 char fbuf[4096];
00875 off_t lastwritten = flength + vlength - length;
00876
00877
00878 fseeko(ff, flength - sizeof(fbuf), SEEK_SET);
00879 while (offset < ftello(ff)) {
00880 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
00881 ast_log(LOG_ERROR, "Short read?!!\n");
00882 fclose(ff);
00883 return -1;
00884 }
00885
00886
00887
00888 fseeko(ff, vlength - length - sizeof(fbuf), SEEK_CUR);
00889
00890
00891 lastwritten = ftello(ff);
00892
00893 if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
00894 ast_log(LOG_ERROR, "Short write?!!\n");
00895 fclose(ff);
00896 return -1;
00897 }
00898
00899 if (lastwritten < offset + sizeof(fbuf)) {
00900 break;
00901 }
00902
00903
00904
00905
00906
00907
00908 fseeko(ff, 2 * sizeof(fbuf) + vlength - length, SEEK_CUR);
00909 }
00910
00911
00912 if (fseeko(ff, offset + length, SEEK_SET)) {
00913 ast_log(LOG_WARNING, "Unable to seek to %" PRId64 " + %" PRId64 " != %" PRId64 "?)\n", offset, length, ftello(ff));
00914 }
00915
00916
00917 ast_debug(1, "Reading at %" PRId64 "\n", ftello(ff));
00918 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
00919 ast_log(LOG_ERROR, "Short read?!!\n");
00920 }
00921 fseek(ff, offset, SEEK_SET);
00922
00923 if (fwrite(value, 1, vlength, ff) < vlength) {
00924 ast_log(LOG_ERROR, "Short write?!!\n");
00925 } else {
00926 off_t curpos = ftello(ff);
00927 foplen = lastwritten - curpos;
00928 if (fwrite(fbuf, 1, foplen, ff) < foplen) {
00929 ast_log(LOG_ERROR, "Short write?!!\n");
00930 }
00931 }
00932 fclose(ff);
00933 }
00934 } else {
00935 enum file_format newline_format = FF_UNKNOWN;
00936
00937
00938 if (args.argc == 5) {
00939 if (tolower(args.format[0]) == 'u') {
00940 newline_format = FF_UNIX;
00941 } else if (tolower(args.format[0]) == 'm') {
00942 newline_format = FF_MAC;
00943 } else if (tolower(args.format[0]) == 'd') {
00944 newline_format = FF_DOS;
00945 }
00946 }
00947 if (newline_format == FF_UNKNOWN && (newline_format = file2format(args.filename)) == FF_UNKNOWN) {
00948 ast_log(LOG_ERROR, "File '%s' not in line format\n", args.filename);
00949 return -1;
00950 }
00951
00952 if (strchr(args.options, 'a')) {
00953
00954 if (!(ff = fopen(args.filename, "a"))) {
00955 ast_log(LOG_ERROR, "Unable to open '%s' for appending: %s\n", args.filename, strerror(errno));
00956 return -1;
00957 }
00958 if (fwrite(value, 1, vlength, ff) < vlength) {
00959 ast_log(LOG_ERROR, "Short write?!!\n");
00960 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) {
00961 ast_log(LOG_ERROR, "Short write?!!\n");
00962 }
00963 fclose(ff);
00964 } else if (offset == 0 && length == LLONG_MAX) {
00965
00966 off_t truncsize;
00967 if (!(ff = fopen(args.filename, "w"))) {
00968 ast_log(LOG_ERROR, "Unable to open '%s' for writing: %s\n", args.filename, strerror(errno));
00969 return -1;
00970 }
00971 if (fwrite(value, 1, vlength, ff) < vlength) {
00972 ast_log(LOG_ERROR, "Short write?!!\n");
00973 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) {
00974 ast_log(LOG_ERROR, "Short write?!!\n");
00975 }
00976 if ((truncsize = ftello(ff)) < 0) {
00977 ast_log(AST_LOG_ERROR, "Unable to determine truncate position of '%s': %s\n", args.filename, strerror(errno));
00978 }
00979 fclose(ff);
00980 if (truncsize >= 0 && truncate(args.filename, truncsize)) {
00981 ast_log(LOG_ERROR, "Unable to truncate file '%s': %s\n", args.filename, strerror(errno));
00982 return -1;
00983 }
00984 } else {
00985 int64_t offset_offset = (offset == 0 ? 0 : -1), length_offset = -1, flength, i, current_length = 0;
00986 char dos_state = 0, fbuf[4096];
00987
00988 if (offset < 0 && length < offset) {
00989
00990 ast_log(LOG_ERROR, "Length cannot specify a position prior to the offset\n");
00991 return -1;
00992 }
00993
00994 if (!(ff = fopen(args.filename, "r+"))) {
00995 ast_log(LOG_ERROR, "Cannot open '%s' for modification: %s\n", args.filename, strerror(errno));
00996 return -1;
00997 }
00998
00999 if (fseek(ff, 0, SEEK_END)) {
01000 ast_log(LOG_ERROR, "Cannot seek to end of file '%s': %s\n", args.filename, strerror(errno));
01001 fclose(ff);
01002 return -1;
01003 }
01004 if ((flength = ftello(ff)) < 0) {
01005 ast_log(AST_LOG_ERROR, "Cannot determine end position of file '%s': %s\n", args.filename, strerror(errno));
01006 fclose(ff);
01007 return -1;
01008 }
01009
01010
01011 if (offset < 0 || length < 0) {
01012 int64_t count = 0;
01013 for (i = (flength / sizeof(fbuf)) * sizeof(fbuf); i >= 0; i -= sizeof(fbuf)) {
01014 char *pos;
01015 if (fseeko(ff, i, SEEK_SET)) {
01016 ast_log(LOG_ERROR, "Cannot seek to offset %" PRId64 ": %s\n", i, strerror(errno));
01017 }
01018 if (i + sizeof(fbuf) >= flength) {
01019 memset(fbuf, 0, sizeof(fbuf));
01020 }
01021 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
01022 ast_log(LOG_ERROR, "Short read: %s\n", strerror(errno));
01023 fclose(ff);
01024 return -1;
01025 }
01026 for (pos = fbuf + sizeof(fbuf) - 1; pos > fbuf - 1; pos--) {
01027 LINE_COUNTER(pos, newline_format, count);
01028
01029 if (length < 0 && count * -1 == length) {
01030 length_offset = i + (pos - fbuf);
01031 } else if (offset < 0 && count * -1 == (offset - 1)) {
01032
01033 if (newline_format == FF_DOS) {
01034 offset_offset = i + (pos - fbuf) + 2;
01035 } else {
01036 offset_offset = i + (pos - fbuf) + 1;
01037 }
01038 break;
01039 }
01040 }
01041 if ((offset < 0 && offset_offset >= 0) || (offset >= 0 && length_offset >= 0)) {
01042 break;
01043 }
01044 }
01045
01046 if (offset < 0 && offset_offset < 0 && offset == count * -1) {
01047 offset_offset = 0;
01048 }
01049 }
01050
01051
01052 if (offset > 0) {
01053 int64_t count = 0;
01054 fseek(ff, 0, SEEK_SET);
01055 for (i = 0; i < flength; i += sizeof(fbuf)) {
01056 char *pos;
01057 if (i + sizeof(fbuf) >= flength) {
01058 memset(fbuf, 0, sizeof(fbuf));
01059 }
01060 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
01061 ast_log(LOG_ERROR, "Short read?!!\n");
01062 fclose(ff);
01063 return -1;
01064 }
01065 for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) {
01066 LINE_COUNTER(pos, newline_format, count);
01067
01068 if (count == offset) {
01069 offset_offset = i + (pos - fbuf) + 1;
01070 break;
01071 }
01072 }
01073 if (offset_offset >= 0) {
01074 break;
01075 }
01076 }
01077 }
01078
01079 if (offset_offset < 0) {
01080 ast_log(LOG_ERROR, "Offset '%s' refers to before the beginning of the file!\n", args.offset);
01081 fclose(ff);
01082 return -1;
01083 }
01084
01085 if (length == 0) {
01086 length_offset = offset_offset;
01087 } else if (length == LLONG_MAX) {
01088 length_offset = flength;
01089 }
01090
01091
01092 if (length_offset < 0) {
01093 fseeko(ff, offset_offset, SEEK_SET);
01094 for (i = offset_offset; i < flength; i += sizeof(fbuf)) {
01095 char *pos;
01096 if (i + sizeof(fbuf) >= flength) {
01097 memset(fbuf, 0, sizeof(fbuf));
01098 }
01099 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
01100 ast_log(LOG_ERROR, "Short read?!!\n");
01101 fclose(ff);
01102 return -1;
01103 }
01104 for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) {
01105 LINE_COUNTER(pos, newline_format, current_length);
01106
01107 if (current_length == length) {
01108 length_offset = i + (pos - fbuf) + 1;
01109 break;
01110 }
01111 }
01112 if (length_offset >= 0) {
01113 break;
01114 }
01115 }
01116 if (length_offset < 0) {
01117
01118 ast_debug(3, "Exceeds length of file? length=%" PRId64 ", count=%" PRId64 ", flength=%" PRId64 "\n", length, current_length, flength);
01119 length_offset = flength;
01120 }
01121 }
01122
01123
01124 if (length_offset - offset_offset == vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format)))) {
01125
01126 fseeko(ff, offset_offset, SEEK_SET);
01127 if (fwrite(value, 1, vlength, ff) < vlength) {
01128 ast_log(LOG_ERROR, "Short write?!!\n");
01129 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) {
01130 ast_log(LOG_ERROR, "Short write?!!\n");
01131 }
01132 fclose(ff);
01133 } else if (length_offset - offset_offset > vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format)))) {
01134
01135 off_t cur;
01136 int64_t length_length = length_offset - offset_offset;
01137 size_t vlen = vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format)));
01138
01139 ast_debug(3, "offset=%s/%" PRId64 ", length=%s/%" PRId64 " (%" PRId64 "), vlength=%" PRId64 ", flength=%" PRId64 "\n",
01140 args.offset, offset_offset, args.length, length_offset, length_length, vlength, flength);
01141
01142 fseeko(ff, offset_offset, SEEK_SET);
01143 if (fwrite(value, 1, vlength, ff) < vlength) {
01144 ast_log(LOG_ERROR, "Short write?!!\n");
01145 fclose(ff);
01146 return -1;
01147 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, vlen - vlength, ff) < vlen - vlength) {
01148 ast_log(LOG_ERROR, "Short write?!!\n");
01149 fclose(ff);
01150 return -1;
01151 }
01152 while ((cur = ftello(ff)) < flength) {
01153 if (cur < 0) {
01154 ast_log(AST_LOG_ERROR, "Unable to determine last write position for '%s': %s\n", args.filename, strerror(errno));
01155 fclose(ff);
01156 return -1;
01157 }
01158 fseeko(ff, length_length - vlen, SEEK_CUR);
01159 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
01160 ast_log(LOG_ERROR, "Short read?!!\n");
01161 fclose(ff);
01162 return -1;
01163 }
01164
01165 fseeko(ff, cur, SEEK_SET);
01166 if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
01167 ast_log(LOG_ERROR, "Short write?!!\n");
01168 fclose(ff);
01169 return -1;
01170 }
01171 }
01172 fclose(ff);
01173 if (truncate(args.filename, flength - (length_length - vlen))) {
01174 ast_log(LOG_ERROR, "Truncation of file failed: %s\n", strerror(errno));
01175 }
01176 } else {
01177
01178 size_t vlen = vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format)));
01179 int64_t origlen = length_offset - offset_offset;
01180 off_t lastwritten = flength + vlen - origlen;
01181
01182 ast_debug(3, "offset=%s/%" PRId64 ", length=%s/%" PRId64 ", vlength=%" PRId64 ", flength=%" PRId64 "\n",
01183 args.offset, offset_offset, args.length, length_offset, vlength, flength);
01184
01185 fseeko(ff, flength - sizeof(fbuf), SEEK_SET);
01186 while (offset_offset + sizeof(fbuf) < ftello(ff)) {
01187 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
01188 ast_log(LOG_ERROR, "Short read?!!\n");
01189 fclose(ff);
01190 return -1;
01191 }
01192 fseeko(ff, sizeof(fbuf) - vlen - origlen, SEEK_CUR);
01193 if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
01194 ast_log(LOG_ERROR, "Short write?!!\n");
01195 fclose(ff);
01196 return -1;
01197 }
01198 if ((lastwritten = ftello(ff) - sizeof(fbuf)) < offset_offset + sizeof(fbuf)) {
01199 break;
01200 }
01201 fseeko(ff, 2 * sizeof(fbuf) + vlen - origlen, SEEK_CUR);
01202 }
01203 fseek(ff, length_offset, SEEK_SET);
01204 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
01205 ast_log(LOG_ERROR, "Short read?!!\n");
01206 fclose(ff);
01207 return -1;
01208 }
01209 fseek(ff, offset_offset, SEEK_SET);
01210 if (fwrite(value, 1, vlength, ff) < vlength) {
01211 ast_log(LOG_ERROR, "Short write?!!\n");
01212 fclose(ff);
01213 return -1;
01214 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) {
01215 ast_log(LOG_ERROR, "Short write?!!\n");
01216 fclose(ff);
01217 return -1;
01218 } else {
01219 off_t curpos = ftello(ff);
01220 foplen = lastwritten - curpos;
01221 if (fwrite(fbuf, 1, foplen, ff) < foplen) {
01222 ast_log(LOG_ERROR, "Short write?!!\n");
01223 }
01224 }
01225 fclose(ff);
01226 }
01227 }
01228 }
01229
01230 return 0;
01231 }
01232
01233 static struct ast_custom_function env_function = {
01234 .name = "ENV",
01235 .read = env_read,
01236 .write = env_write
01237 };
01238
01239 static struct ast_custom_function stat_function = {
01240 .name = "STAT",
01241 .read = stat_read,
01242 .read_max = 12,
01243 };
01244
01245 static struct ast_custom_function file_function = {
01246 .name = "FILE",
01247 .read2 = file_read,
01248 .write = file_write,
01249 };
01250
01251 static struct ast_custom_function file_count_line_function = {
01252 .name = "FILE_COUNT_LINE",
01253 .read2 = file_count_line,
01254 .read_max = 12,
01255 };
01256
01257 static struct ast_custom_function file_format_function = {
01258 .name = "FILE_FORMAT",
01259 .read2 = file_format,
01260 .read_max = 2,
01261 };
01262
01263 static int unload_module(void)
01264 {
01265 int res = 0;
01266
01267 res |= ast_custom_function_unregister(&env_function);
01268 res |= ast_custom_function_unregister(&stat_function);
01269 res |= ast_custom_function_unregister(&file_function);
01270 res |= ast_custom_function_unregister(&file_count_line_function);
01271 res |= ast_custom_function_unregister(&file_format_function);
01272
01273 return res;
01274 }
01275
01276 static int load_module(void)
01277 {
01278 int res = 0;
01279
01280 res |= ast_custom_function_register(&env_function);
01281 res |= ast_custom_function_register_escalating(&stat_function, AST_CFE_READ);
01282 res |= ast_custom_function_register_escalating(&file_function, AST_CFE_BOTH);
01283 res |= ast_custom_function_register_escalating(&file_count_line_function, AST_CFE_READ);
01284 res |= ast_custom_function_register_escalating(&file_format_function, AST_CFE_READ);
01285
01286 return res;
01287 }
01288
01289 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Environment/filesystem dialplan functions");