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