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