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