Asterisk - The Open Source Telephony Project GIT-master-545c459
Loading...
Searching...
No Matches
func_env.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 1999 - 2010, Digium, Inc.
5 *
6 * See http://www.asterisk.org for more information about
7 * the Asterisk project. Please do not directly contact
8 * any of the maintainers of this project for assistance;
9 * the project provides a web site, mailing lists and IRC
10 * channels for your use.
11 *
12 * This program is free software, distributed under the terms of
13 * the GNU General Public License Version 2. See the LICENSE file
14 * at the top of the source tree.
15 */
16
17/*! \file
18 *
19 * \brief Environment related dialplan functions
20 *
21 * \ingroup functions
22 */
23
24/*** MODULEINFO
25 <support_level>core</support_level>
26 ***/
27
28#include "asterisk.h"
29
30#include <sys/stat.h> /* stat(2) */
31#include <libgen.h> /* dirname and basename */
32
33#include "asterisk/module.h"
34#include "asterisk/channel.h"
35#include "asterisk/pbx.h"
36#include "asterisk/utils.h"
37#include "asterisk/app.h"
38#include "asterisk/file.h"
39
40/*** DOCUMENTATION
41 <function name="ENV" language="en_US">
42 <since>
43 <version>1.2.0</version>
44 </since>
45 <synopsis>
46 Gets or sets the environment variable specified.
47 </synopsis>
48 <syntax>
49 <parameter name="varname" required="true">
50 <para>Environment variable name</para>
51 </parameter>
52 </syntax>
53 <description>
54 <para>Variables starting with <literal>AST_</literal> are reserved to the system and may not be set.</para>
55 <para>Additionally, the following system variables are available as special built-in dialplan variables.
56 These variables cannot be set or modified and are read-only.</para>
57 <variablelist>
58 <variable name="EPOCH">
59 <para>Current unix style epoch</para>
60 </variable>
61 <variable name="SYSTEMNAME">
62 <para>value of the <literal>systemname</literal> option from <literal>asterisk.conf</literal></para>
63 </variable>
64 <variable name="ASTCACHEDIR">
65 <para>value of the <literal>astcachedir</literal> option from <literal>asterisk.conf</literal></para>
66 </variable>
67 <variable name="ASTETCDIR">
68 <para>value of the <literal>astetcdir</literal> option from <literal>asterisk.conf</literal></para>
69 </variable>
70 <variable name="ASTMODDIR">
71 <para>value of the <literal>astmoddir</literal> option from <literal>asterisk.conf</literal></para>
72 </variable>
73 <variable name="ASTVARLIBDIR">
74 <para>value of the <literal>astvarlib</literal> option from <literal>asterisk.conf</literal></para>
75 </variable>
76 <variable name="ASTDBDIR">
77 <para>value of the <literal>astdbdir</literal> option from <literal>asterisk.conf</literal></para>
78 </variable>
79 <variable name="ASTKEYDIR">
80 <para>value of the <literal>astkeydir</literal> option from <literal>asterisk.conf</literal></para>
81 </variable>
82 <variable name="ASTDATADIR">
83 <para>value of the <literal>astdatadir</literal> option from <literal>asterisk.conf</literal></para>
84 </variable>
85 <variable name="ASTAGIDIR">
86 <para>value of the <literal>astagidir</literal> option from <literal>asterisk.conf</literal></para>
87 </variable>
88 <variable name="ASTSPOOLDIR">
89 <para>value of the <literal>astspooldir</literal> option from <literal>asterisk.conf</literal></para>
90 </variable>
91 <variable name="ASTRUNDIR">
92 <para>value of the <literal>astrundir</literal> option from <literal>asterisk.conf</literal></para>
93 </variable>
94 <variable name="ASTLOGDIR">
95 <para>value of the <literal>astlogdir</literal> option from <literal>asterisk.conf</literal></para>
96 </variable>
97 <variable name="ASTSBINDIR">
98 <para>value of the <literal>astsbindir</literal> option from <literal>asterisk.conf</literal></para>
99 </variable>
100 <variable name="ENTITYID">
101 <para>Global Entity ID set automatically, or from <literal>asterisk.conf</literal></para>
102 </variable>
103 </variablelist>
104 </description>
105 </function>
106 <function name="STAT" language="en_US">
107 <since>
108 <version>1.4.0</version>
109 </since>
110 <synopsis>
111 Does a check on the specified file.
112 </synopsis>
113 <syntax>
114 <parameter name="flag" required="true">
115 <para>Flag may be one of the following:</para>
116 <para>d - Checks if the file is a directory.</para>
117 <para>e - Checks if the file exists.</para>
118 <para>f - Checks if the file is a regular file.</para>
119 <para>m - Returns the file mode (in octal)</para>
120 <para>s - Returns the size (in bytes) of the file</para>
121 <para>A - Returns the epoch at which the file was last accessed.</para>
122 <para>C - Returns the epoch at which the inode was last changed.</para>
123 <para>M - Returns the epoch at which the file was last modified.</para>
124 </parameter>
125 <parameter name="filename" required="true" />
126 </syntax>
127 <description>
128 <note>
129 <para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
130 is set to <literal>no</literal>, this function can only be executed from the
131 dialplan, and not directly from external protocols.</para>
132 </note>
133 </description>
134 </function>
135 <function name="FILE" language="en_US">
136 <since>
137 <version>1.6.0</version>
138 </since>
139 <synopsis>
140 Read or write text file.
141 </synopsis>
142 <syntax>
143 <parameter name="filename" required="true" />
144 <parameter name="offset">
145 <para>Maybe specified as any number. If negative, <replaceable>offset</replaceable> specifies the number
146 of bytes back from the end of the file.</para>
147 <note>
148 <para>Line offsets begin at 0, not 1.</para>
149 </note>
150 </parameter>
151 <parameter name="length">
152 <para>If specified, will limit the length of the data read to that size. If negative,
153 trims <replaceable>length</replaceable> bytes from the end of the file.</para>
154 </parameter>
155 <parameter name="options">
156 <optionlist>
157 <option name="l">
158 <para>Line mode: offset and length are assumed to be
159 measured in lines, instead of byte offsets.</para>
160 </option>
161 <option name="a">
162 <para>In write mode only, the append option is used to
163 append to the end of the file, instead of overwriting
164 the existing file.</para>
165 </option>
166 <option name="d">
167 <para>In write mode and line mode only, this option does
168 not automatically append a newline string to the end of
169 a value. This is useful for deleting lines, instead of
170 setting them to blank.</para>
171 </option>
172 </optionlist>
173 </parameter>
174 <parameter name="format">
175 <para>The <replaceable>format</replaceable> parameter may be
176 used to delimit the type of line terminators in line mode.</para>
177 <optionlist>
178 <option name="u">
179 <para>Unix newline format (LF).</para>
180 </option>
181 <option name="d">
182 <para>DOS newline format (CR LF).</para>
183 </option>
184 <option name="m">
185 <para>Macintosh newline format (CR).</para>
186 </option>
187 </optionlist>
188 </parameter>
189 </syntax>
190 <description>
191 <para>Read and write text file in character and line mode.</para>
192 <para>Examples:</para>
193 <para>Read mode (byte):</para>
194 <example title="Reads the entire content of the file">
195 same => n,Set(foo=${FILE(/tmp/test.txt)})
196 </example>
197 <example title="Reads from the 11th byte to the end of the file (i.e. skips the first 10)">
198 same => n,Set(foo=${FILE(/tmp/test.txt,10)})
199 </example>
200 <example title="Reads from the 11th to 20th byte in the file (i.e. skip the first 10, then read 10 bytes)">
201 same => n,Set(foo=${FILE(/tmp/test.txt,10,10)})
202 </example>
203 <para>Read mode (line):</para>
204 <example title="Reads the 3rd line of the file">
205 same => n,Set(foo=${FILE(/tmp/test.txt,3,1,l)})
206 </example>
207 <example title="Reads the 3rd and 4th lines of the file">
208 same => n,Set(foo=${FILE(/tmp/test.txt,3,2,l)})
209 </example>
210 <example title="Reads from the third line to the end of the file">
211 same => n,Set(foo=${FILE(/tmp/test.txt,3,,l)})
212 </example>
213 <example title="Reads the last three lines of the file">
214 same => n,Set(foo=${FILE(/tmp/test.txt,-3,,l)})
215 </example>
216 <example title="Reads the 3rd line of a DOS-formatted file">
217 same => n,Set(foo=${FILE(/tmp/test.txt,3,1,l,d)})
218 </example>
219 <para>Write mode (byte):</para>
220 <example title="Truncate the file and write bar">
221 same => n,Set(FILE(/tmp/test.txt)=bar)
222 </example>
223 <example title="Append bar">
224 same => n,Set(FILE(/tmp/test.txt,,,a)=bar)
225 </example>
226 <example title="Replace the first byte with bar (replaces 1 character with 3)">
227 same => n,Set(FILE(/tmp/test.txt,0,1)=bar)
228 </example>
229 <example title="Replace 10 bytes beginning at the 21st byte of the file with bar">
230 same => n,Set(FILE(/tmp/test.txt,20,10)=bar)
231 </example>
232 <example title="Replace all bytes from the 21st with bar">
233 same => n,Set(FILE(/tmp/test.txt,20)=bar)
234 </example>
235 <example title="Insert bar after the 4th character">
236 same => n,Set(FILE(/tmp/test.txt,4,0)=bar)
237 </example>
238 <para>Write mode (line):</para>
239 <example title="Replace the first line of the file with bar">
240 same => n,Set(FILE(/tmp/foo.txt,0,1,l)=bar)
241 </example>
242 <example title="Replace the last line of the file with bar">
243 same => n,Set(FILE(/tmp/foo.txt,-1,,l)=bar)
244 </example>
245 <example title="Append bar to the file with a newline">
246 same => n,Set(FILE(/tmp/foo.txt,,,al)=bar)
247 </example>
248 <note>
249 <para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
250 is set to <literal>no</literal>, this function can only be executed from the
251 dialplan, and not directly from external protocols.</para>
252 </note>
253 </description>
254 <see-also>
255 <ref type="function">FILE_COUNT_LINE</ref>
256 <ref type="function">FILE_FORMAT</ref>
257 </see-also>
258 </function>
259 <function name="FILE_COUNT_LINE" language="en_US">
260 <since>
261 <version>1.8.0</version>
262 </since>
263 <synopsis>
264 Obtains the number of lines of a text file.
265 </synopsis>
266 <syntax>
267 <parameter name="filename" required="true" />
268 <parameter name="format">
269 <para>Format may be one of the following:</para>
270 <optionlist>
271 <option name="u">
272 <para>Unix newline format.</para>
273 </option>
274 <option name="d">
275 <para>DOS newline format.</para>
276 </option>
277 <option name="m">
278 <para>Macintosh newline format.</para>
279 </option>
280 </optionlist>
281 <note><para>If not specified, an attempt will be made to determine the newline format type.</para></note>
282 </parameter>
283 </syntax>
284 <description>
285 <para>Returns the number of lines, or <literal>-1</literal> on error.</para>
286 <note>
287 <para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
288 is set to <literal>no</literal>, this function can only be executed from the
289 dialplan, and not directly from external protocols.</para>
290 </note>
291 </description>
292 <see-also>
293 <ref type="function">FILE</ref>
294 <ref type="function">FILE_FORMAT</ref>
295 </see-also>
296 </function>
297 <function name="FILE_FORMAT" language="en_US">
298 <since>
299 <version>1.8.0</version>
300 </since>
301 <synopsis>
302 Return the newline format of a text file.
303 </synopsis>
304 <syntax>
305 <parameter name="filename" required="true" />
306 </syntax>
307 <description>
308 <para>Return the line terminator type:</para>
309 <para>'u' - Unix "\n" format</para>
310 <para>'d' - DOS "\r\n" format</para>
311 <para>'m' - Macintosh "\r" format</para>
312 <para>'x' - Cannot be determined</para>
313 <note>
314 <para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
315 is set to <literal>no</literal>, this function can only be executed from the
316 dialplan, and not directly from external protocols.</para>
317 </note>
318 </description>
319 <see-also>
320 <ref type="function">FILE</ref>
321 <ref type="function">FILE_COUNT_LINE</ref>
322 </see-also>
323 </function>
324 <function name="BASENAME" language="en_US">
325 <since>
326 <version>16.21.0</version>
327 <version>18.7.0</version>
328 </since>
329 <synopsis>
330 Return the name of a file.
331 </synopsis>
332 <syntax>
333 <parameter name="filename" required="true" />
334 </syntax>
335 <description>
336 <para>Return the base file name, given a full file path.</para>
337 <example title="Directory name">
338 same => n,Set(basename=${BASENAME(/etc/asterisk/extensions.conf)})
339 same => n,NoOp(${basename}) ; outputs extensions.conf
340 </example>
341 </description>
342 <see-also>
343 <ref type="function">DIRNAME</ref>
344 </see-also>
345 </function>
346 <function name="DIRNAME" language="en_US">
347 <since>
348 <version>16.21.0</version>
349 <version>18.7.0</version>
350 </since>
351 <synopsis>
352 Return the directory of a file.
353 </synopsis>
354 <syntax>
355 <parameter name="filename" required="true" />
356 </syntax>
357 <description>
358 <para>Return the directory of a file, given a full file path.</para>
359 <example title="Directory name">
360 same => n,Set(dirname=${DIRNAME(/etc/asterisk/extensions.conf)})
361 same => n,NoOp(${dirname}) ; outputs /etc/asterisk
362 </example>
363 </description>
364 <see-also>
365 <ref type="function">BASENAME</ref>
366 </see-also>
367 </function>
368 ***/
369
370static int env_read(struct ast_channel *chan, const char *cmd, char *data,
371 char *buf, size_t len)
372{
373 char *ret = NULL;
374
375 *buf = '\0';
376
377 if (data)
378 ret = getenv(data);
379
380 if (ret)
381 ast_copy_string(buf, ret, len);
382
383 return 0;
384}
385
386static int env_write(struct ast_channel *chan, const char *cmd, char *data,
387 const char *value)
388{
389 if (!ast_strlen_zero(data) && strncmp(data, "AST_", 4)) {
390 if (!ast_strlen_zero(value)) {
391 setenv(data, value, 1);
392 } else {
393 unsetenv(data);
394 }
395 }
396
397 return 0;
398}
399
400static int stat_read(struct ast_channel *chan, const char *cmd, char *data,
401 char *buf, size_t len)
402{
403 char *action;
404 struct stat s;
405
406 ast_copy_string(buf, "0", len);
407
408 action = strsep(&data, ",");
409 if (stat(data, &s)) {
410 return 0;
411 } else {
412 switch (*action) {
413 case 'e':
414 strcpy(buf, "1");
415 break;
416 case 's':
417 snprintf(buf, len, "%u", (unsigned int) s.st_size);
418 break;
419 case 'f':
420 snprintf(buf, len, "%d", S_ISREG(s.st_mode) ? 1 : 0);
421 break;
422 case 'd':
423 snprintf(buf, len, "%d", S_ISDIR(s.st_mode) ? 1 : 0);
424 break;
425 case 'M':
426 snprintf(buf, len, "%d", (int) s.st_mtime);
427 break;
428 case 'A':
429 snprintf(buf, len, "%d", (int) s.st_mtime);
430 break;
431 case 'C':
432 snprintf(buf, len, "%d", (int) s.st_ctime);
433 break;
434 case 'm':
435 snprintf(buf, len, "%o", (unsigned int) s.st_mode);
436 break;
437 }
438 }
439
440 return 0;
441}
442
449
450static int64_t count_lines(const char *filename, enum file_format newline_format)
451{
452 int count = 0;
453 char fbuf[4096];
454 FILE *ff;
455
456 if (!(ff = fopen(filename, "r"))) {
457 ast_log(LOG_ERROR, "Unable to open '%s': %s\n", filename, strerror(errno));
458 return -1;
459 }
460
461 while (fgets(fbuf, sizeof(fbuf), ff)) {
462 char *next = fbuf, *first_cr = NULL, *first_nl = NULL;
463
464 /* Must do it this way, because if the fileformat is FF_MAC, then Unix
465 * assumptions about line-format will not come into play. */
466 while (next) {
467 if (newline_format == FF_DOS || newline_format == FF_MAC || newline_format == FF_UNKNOWN) {
468 first_cr = strchr(next, '\r');
469 }
470 if (newline_format == FF_UNIX || newline_format == FF_UNKNOWN) {
471 first_nl = strchr(next, '\n');
472 }
473
474 /* No terminators found in buffer */
475 if (!first_cr && !first_nl) {
476 break;
477 }
478
479 if (newline_format == FF_UNKNOWN) {
480 if ((first_cr && !first_nl) || (first_cr && first_cr < first_nl)) {
481 if (first_nl && first_nl == first_cr + 1) {
482 newline_format = FF_DOS;
483 } else if (first_cr && first_cr == &fbuf[sizeof(fbuf) - 2]) {
484 /* Get it on the next pass */
485 fseek(ff, -1, SEEK_CUR);
486 break;
487 } else {
488 newline_format = FF_MAC;
489 first_nl = NULL;
490 }
491 } else {
492 newline_format = FF_UNIX;
493 first_cr = NULL;
494 }
495 /* Jump down into next section */
496 }
497
498 if (newline_format == FF_DOS) {
499 if (first_nl && first_cr && first_nl == first_cr + 1) {
500 next = first_nl + 1;
501 count++;
502 } else if (first_cr == &fbuf[sizeof(fbuf) - 2]) {
503 /* Get it on the next pass */
504 fseek(ff, -1, SEEK_CUR);
505 break;
506 }
507 } else if (newline_format == FF_MAC) {
508 if (first_cr) {
509 next = first_cr + 1;
510 count++;
511 }
512 } else if (newline_format == FF_UNIX) {
513 if (first_nl) {
514 next = first_nl + 1;
515 count++;
516 }
517 }
518 }
519 }
520 fclose(ff);
521
522 return count;
523}
524
525static int file_count_line(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
526{
527 enum file_format newline_format = FF_UNKNOWN;
528 int64_t count;
530 AST_APP_ARG(filename);
531 AST_APP_ARG(format);
532 );
533
535 if (args.argc > 1) {
536 if (tolower(args.format[0]) == 'd') {
537 newline_format = FF_DOS;
538 } else if (tolower(args.format[0]) == 'm') {
539 newline_format = FF_MAC;
540 } else if (tolower(args.format[0]) == 'u') {
541 newline_format = FF_UNIX;
542 }
543 }
544
545 count = count_lines(args.filename, newline_format);
546 ast_str_set(buf, len, "%" PRId64, count);
547 return 0;
548}
549
550#define LINE_COUNTER(cptr, term, counter) \
551 if (*cptr == '\n' && term == FF_UNIX) { \
552 counter++; \
553 } else if (*cptr == '\r' && term == FF_DOS && dos_state == 0) { \
554 dos_state = 1; \
555 } else if (*cptr == '\n' && term == FF_DOS && dos_state == 1) { \
556 dos_state = 0; \
557 counter++; \
558 } else if (*cptr == '\r' && term == FF_MAC) { \
559 counter++; \
560 } else if (term == FF_DOS) { \
561 dos_state = 0; \
562 }
563
564static enum file_format file2format(const char *filename)
565{
566 FILE *ff;
567 char fbuf[4096];
568 char *first_cr, *first_nl;
569 enum file_format newline_format = FF_UNKNOWN;
570
571 if (!(ff = fopen(filename, "r"))) {
572 ast_log(LOG_ERROR, "Cannot open '%s': %s\n", filename, strerror(errno));
573 return -1;
574 }
575
576 while (fgets(fbuf, sizeof(fbuf), ff)) {
577 first_cr = strchr(fbuf, '\r');
578 first_nl = strchr(fbuf, '\n');
579
580 if (!first_cr && !first_nl) {
581 continue;
582 }
583
584 if ((first_cr && !first_nl) || (first_cr && first_cr < first_nl)) {
585
586 if (first_nl && first_nl == first_cr + 1) {
587 newline_format = FF_DOS;
588 } else if (first_cr && first_cr == &fbuf[sizeof(fbuf) - 2]) {
589 /* Edge case: get it on the next pass */
590 fseek(ff, -1, SEEK_CUR);
591 continue;
592 } else {
593 newline_format = FF_MAC;
594 }
595 } else {
596 newline_format = FF_UNIX;
597 }
598 break;
599 }
600 fclose(ff);
601 return newline_format;
602}
603
604static int file_format(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
605{
606 enum file_format newline_format = file2format(data);
607 ast_str_set(buf, len, "%c", newline_format == FF_UNIX ? 'u' : newline_format == FF_DOS ? 'd' : newline_format == FF_MAC ? 'm' : 'x');
608 return 0;
609}
610
611static int file_dirname(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
612{
613 char *ret = NULL;
614
615 *buf = '\0';
616
617 if (data) {
618 ret = dirname(data);
619 }
620
621 if (ret) {
622 ast_copy_string(buf, ret, len);
623 }
624
625 return 0;
626}
627
628static int file_basename(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
629{
630 char *ret = NULL;
631
632 *buf = '\0';
633
634 if (data) {
635 ret = basename(data);
636 }
637
638 if (ret) {
639 ast_copy_string(buf, ret, len);
640 }
641
642 return 0;
643}
644
645static int file_read(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
646{
647 FILE *ff;
648 int64_t offset = 0, length = LLONG_MAX;
649 enum file_format format = FF_UNKNOWN;
650 char fbuf[4096];
651 int64_t flength, i; /* iterator needs to be signed, so it can go negative and terminate the loop */
652 int64_t offset_offset = -1, length_offset = -1;
653 char dos_state = 0;
655 AST_APP_ARG(filename);
656 AST_APP_ARG(offset);
657 AST_APP_ARG(length);
659 AST_APP_ARG(fileformat);
660 );
661
663
664 if (args.argc > 1) {
665 sscanf(args.offset, "%" SCNd64, &offset);
666 }
667 if (args.argc > 2) {
668 sscanf(args.length, "%" SCNd64, &length);
669 }
670
671 if (args.argc < 4 || !strchr(args.options, 'l')) {
672 /* Character-based mode */
673 off_t off_i;
674
675 if (!(ff = fopen(args.filename, "r"))) {
676 ast_log(LOG_WARNING, "Cannot open file '%s' for reading: %s\n", args.filename, strerror(errno));
677 return 0;
678 }
679
680 if (fseeko(ff, 0, SEEK_END) < 0) {
681 ast_log(LOG_ERROR, "Cannot seek to end of '%s': %s\n", args.filename, strerror(errno));
682 fclose(ff);
683 return -1;
684 }
685 flength = ftello(ff);
686
687 if (offset < 0) {
688 fseeko(ff, offset, SEEK_END);
689 if ((offset = ftello(ff)) < 0) {
690 ast_log(AST_LOG_ERROR, "Cannot determine offset position of '%s': %s\n", args.filename, strerror(errno));
691 fclose(ff);
692 return -1;
693 }
694 }
695 if (length < 0) {
696 fseeko(ff, length, SEEK_END);
697 if ((length = ftello(ff)) - offset < 0) {
698 /* Eliminates all results */
699 fclose(ff);
700 return -1;
701 }
702 } else if (length == LLONG_MAX) {
703 fseeko(ff, 0, SEEK_END);
704 length = ftello(ff);
705 }
706
708
709 fseeko(ff, offset, SEEK_SET);
710 for (off_i = ftello(ff); off_i < flength && off_i < offset + length; off_i += sizeof(fbuf)) {
711 /* Calculate if we need to retrieve just a portion of the file in memory */
712 size_t toappend = sizeof(fbuf);
713
714 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
715 ast_log(LOG_ERROR, "Short read?!!\n");
716 break;
717 }
718
719 /* Don't go past the length requested */
720 if (off_i + toappend > offset + length) {
721 toappend = MIN(offset + length - off_i, flength - off_i);
722 }
723
724 ast_str_append_substr(buf, len, fbuf, toappend);
725 }
726 fclose(ff);
727 return 0;
728 }
729
730 /* Line-based read */
731 if (args.argc == 5) {
732 if (tolower(args.fileformat[0]) == 'd') {
733 format = FF_DOS;
734 } else if (tolower(args.fileformat[0]) == 'm') {
735 format = FF_MAC;
736 } else if (tolower(args.fileformat[0]) == 'u') {
737 format = FF_UNIX;
738 }
739 }
740
741 if (format == FF_UNKNOWN) {
742 if ((format = file2format(args.filename)) == FF_UNKNOWN) {
743 ast_log(LOG_WARNING, "'%s' is not a line-based file\n", args.filename);
744 return -1;
745 }
746 }
747
748 if (offset < 0 && length <= offset) {
749 /* Length eliminates all content */
750 return -1;
751 } else if (offset == 0) {
752 offset_offset = 0;
753 }
754
755 if (!(ff = fopen(args.filename, "r"))) {
756 ast_log(LOG_ERROR, "Cannot open '%s': %s\n", args.filename, strerror(errno));
757 return -1;
758 }
759
760 if (fseek(ff, 0, SEEK_END)) {
761 ast_log(LOG_ERROR, "Cannot seek to end of file '%s': %s\n", args.filename, strerror(errno));
762 fclose(ff);
763 return -1;
764 }
765
766 flength = ftello(ff);
767
768 if (length == LLONG_MAX) {
769 length_offset = flength;
770 }
771
772 /* For negative offset and/or negative length */
773 if (offset < 0 || length < 0) {
774 int64_t count = 0;
775 /* Start with an even multiple of fbuf, so at the end of reading with a
776 * 0 offset, we don't try to go past the beginning of the file. */
777 for (i = (flength / sizeof(fbuf)) * sizeof(fbuf); i >= 0; i -= sizeof(fbuf)) {
778 size_t end;
779 char *pos;
780 if (fseeko(ff, i, SEEK_SET)) {
781 ast_log(LOG_ERROR, "Cannot seek to offset %" PRId64 ": %s\n", i, strerror(errno));
782 }
783 end = fread(fbuf, 1, sizeof(fbuf), ff);
784 for (pos = (end < sizeof(fbuf) ? fbuf + end - 1 : fbuf + sizeof(fbuf) - 1); pos >= fbuf; pos--) {
785 LINE_COUNTER(pos, format, count);
786
787 if (length < 0 && count * -1 == length) {
788 length_offset = i + (pos - fbuf);
789 } else if (offset < 0 && count * -1 == (offset - 1)) {
790 /* Found our initial offset. We're done with reverse motion! */
791 if (format == FF_DOS) {
792 offset_offset = i + (pos - fbuf) + 2;
793 } else {
794 offset_offset = i + (pos - fbuf) + 1;
795 }
796 break;
797 }
798 }
799 if ((offset < 0 && offset_offset >= 0) || (offset >= 0 && length_offset >= 0)) {
800 break;
801 }
802 }
803 /* We're at the beginning, and the negative offset indicates the exact number of lines in the file */
804 if (offset < 0 && offset_offset < 0 && offset == count * -1) {
805 offset_offset = 0;
806 }
807 }
808
809 /* Positve line offset */
810 if (offset > 0) {
811 int64_t count = 0;
812 fseek(ff, 0, SEEK_SET);
813 for (i = 0; i < flength; i += sizeof(fbuf)) {
814 char *pos;
815 if (i + sizeof(fbuf) <= flength) {
816 /* Don't let previous values influence current counts, due to short reads */
817 memset(fbuf, 0, sizeof(fbuf));
818 }
819 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
820 ast_log(LOG_ERROR, "Short read?!!\n");
821 fclose(ff);
822 return -1;
823 }
824 for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) {
825 LINE_COUNTER(pos, format, count);
826
827 if (count == offset) {
828 offset_offset = i + (pos - fbuf) + 1;
829 break;
830 }
831 }
832 if (offset_offset >= 0) {
833 break;
834 }
835 }
836 }
837
838 if (offset_offset < 0) {
839 ast_log(LOG_ERROR, "Offset '%s' refers to before the beginning of the file!\n", args.offset);
840 fclose(ff);
841 return -1;
842 }
843
845 if (fseeko(ff, offset_offset, SEEK_SET)) {
846 ast_log(LOG_ERROR, "fseeko failed: %s\n", strerror(errno));
847 }
848
849 /* If we have both offset_offset and length_offset, then grabbing the
850 * buffer is simply a matter of just retrieving the file and adding it
851 * to buf. Otherwise, we need to run byte-by-byte forward until the
852 * length is complete. */
853 if (length_offset >= 0) {
854 ast_debug(3, "offset=%" PRId64 ", length=%" PRId64 ", offset_offset=%" PRId64 ", length_offset=%" PRId64 "\n", offset, length, offset_offset, length_offset);
855 for (i = offset_offset; i < length_offset; i += sizeof(fbuf)) {
856 if (fread(fbuf, 1, i + sizeof(fbuf) > flength ? flength - i : sizeof(fbuf), ff) < (i + sizeof(fbuf) > flength ? flength - i : sizeof(fbuf))) {
857 ast_log(LOG_ERROR, "Short read?!!\n");
858 }
859 ast_debug(3, "Appending first %" PRId64" bytes of fbuf=%s\n", (int64_t)(i + sizeof(fbuf) > length_offset ? length_offset - i : sizeof(fbuf)), fbuf);
860 ast_str_append_substr(buf, len, fbuf, i + sizeof(fbuf) > length_offset ? length_offset - i : sizeof(fbuf));
861 }
862 } else if (length == 0) {
863 /* Nothing to do */
864 } else {
865 /* Positive line offset */
866 int64_t current_length = 0;
867 char dos_state = 0;
868 ast_debug(3, "offset=%" PRId64 ", length=%" PRId64 ", offset_offset=%" PRId64 ", length_offset=%" PRId64 "\n", offset, length, offset_offset, length_offset);
869 for (i = offset_offset; i < flength; i += sizeof(fbuf)) {
870 char *pos;
871 size_t bytes_read;
872 if ((bytes_read = fread(fbuf, 1, sizeof(fbuf), ff)) < sizeof(fbuf) && !feof(ff)) {
873 ast_log(LOG_ERROR, "Short read?!!\n");
874 fclose(ff);
875 return -1;
876 }
877 for (pos = fbuf; pos < fbuf + bytes_read; pos++) {
878 LINE_COUNTER(pos, format, current_length);
879
880 if (current_length == length) {
881 length_offset = i + (pos - fbuf) + 1;
882 break;
883 }
884 }
885 ast_debug(3, "length_offset=%" PRId64 ", length_offset - i=%" PRId64 "\n", length_offset, length_offset - i);
886 ast_str_append_substr(buf, len, fbuf, (length_offset >= 0) ? length_offset - i : (flength > i + sizeof(fbuf)) ? sizeof(fbuf) : flength - i);
887
888 if (length_offset >= 0) {
889 break;
890 }
891 }
892 }
893
894 fclose(ff);
895 return 0;
896}
897
898const char *format2term(enum file_format f) __attribute__((const));
899const char *format2term(enum file_format f)
900{
901 const char *term[] = { "", "\n", "\r\n", "\r" };
902 return term[f + 1];
903}
904
905static int file_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
906{
908 AST_APP_ARG(filename);
909 AST_APP_ARG(offset);
910 AST_APP_ARG(length);
912 AST_APP_ARG(format);
913 );
914 int64_t offset = 0, length = LLONG_MAX;
915 off_t flength, vlength;
916 size_t foplen = 0;
917 FILE *ff;
918
920
921 if (args.argc > 1) {
922 sscanf(args.offset, "%" SCNd64, &offset);
923 }
924 if (args.argc > 2) {
925 sscanf(args.length, "%" SCNd64, &length);
926 }
927
928 vlength = strlen(value);
929
930 if (args.argc < 4 || !strchr(args.options, 'l')) {
931 /* Character-based mode */
932
933 if (args.argc > 3 && strchr(args.options, 'a')) {
934 /* Append mode */
935 if (!(ff = fopen(args.filename, "a"))) {
936 ast_log(LOG_WARNING, "Cannot open file '%s' for appending: %s\n", args.filename, strerror(errno));
937 return 0;
938 }
939 if (fwrite(value, 1, vlength, ff) < vlength) {
940 ast_log(LOG_ERROR, "Short write?!!\n");
941 }
942 fclose(ff);
943 return 0;
944 } else if (offset == 0 && length == LLONG_MAX) {
945 if (!(ff = fopen(args.filename, "w"))) {
946 ast_log(LOG_WARNING, "Cannot open file '%s' for writing: %s\n", args.filename, strerror(errno));
947 return 0;
948 }
949 if (fwrite(value, 1, vlength, ff) < vlength) {
950 ast_log(LOG_ERROR, "Short write?!!\n");
951 }
952 fclose(ff);
953 return 0;
954 }
955
956 if (!(ff = fopen(args.filename, "r+"))) {
957 ast_log(LOG_WARNING, "Cannot open file '%s' for modification: %s\n", args.filename, strerror(errno));
958 return 0;
959 }
960 fseeko(ff, 0, SEEK_END);
961 flength = ftello(ff);
962
963 if (offset < 0) {
964 if (fseeko(ff, offset, SEEK_END)) {
965 ast_log(LOG_ERROR, "Cannot seek to offset of '%s': %s\n", args.filename, strerror(errno));
966 fclose(ff);
967 return -1;
968 }
969 if ((offset = ftello(ff)) < 0) {
970 ast_log(AST_LOG_ERROR, "Cannot determine offset position of '%s': %s\n", args.filename, strerror(errno));
971 fclose(ff);
972 return -1;
973 }
974 }
975
976 if (length < 0) {
977 length = flength - offset + length;
978 if (length < 0) {
979 ast_log(LOG_ERROR, "Length '%s' exceeds the file length. No data will be written.\n", args.length);
980 fclose(ff);
981 return -1;
982 }
983 }
984
985 fseeko(ff, offset, SEEK_SET);
986
987 ast_debug(3, "offset=%s/%" PRId64 ", length=%s/%" PRId64 ", vlength=%" PRId64 ", flength=%" PRId64 "\n",
988 S_OR(args.offset, "(null)"), offset, S_OR(args.length, "(null)"), length, vlength, flength);
989
990 if (length == vlength) {
991 /* Simplest case, a straight replace */
992 if (fwrite(value, 1, vlength, ff) < vlength) {
993 ast_log(LOG_ERROR, "Short write?!!\n");
994 }
995 fclose(ff);
996 } else if (length == LLONG_MAX) {
997 /* Simple truncation */
998 if (fwrite(value, 1, vlength, ff) < vlength) {
999 ast_log(LOG_ERROR, "Short write?!!\n");
1000 }
1001 fclose(ff);
1002 if (truncate(args.filename, offset + vlength)) {
1003 ast_log(LOG_ERROR, "Unable to truncate the file: %s\n", strerror(errno));
1004 }
1005 } else if (length > vlength) {
1006 /* More complex -- need to close a gap */
1007 char fbuf[4096];
1008 off_t cur;
1009 if (fwrite(value, 1, vlength, ff) < vlength) {
1010 ast_log(LOG_ERROR, "Short write?!!\n");
1011 }
1012 fseeko(ff, length - vlength, SEEK_CUR);
1013 while ((cur = ftello(ff)) < flength) {
1014 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
1015 ast_log(LOG_ERROR, "Short read?!!\n");
1016 }
1017 fseeko(ff, cur + vlength - length, SEEK_SET);
1018 if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
1019 ast_log(LOG_ERROR, "Short write?!!\n");
1020 }
1021 /* Seek to where we stopped reading */
1022 if (fseeko(ff, cur + sizeof(fbuf), SEEK_SET) < 0) {
1023 /* Only reason for seek to fail is EOF */
1024 break;
1025 }
1026 }
1027 fclose(ff);
1028 if (truncate(args.filename, flength - (length - vlength))) {
1029 ast_log(LOG_ERROR, "Unable to truncate the file: %s\n", strerror(errno));
1030 }
1031 } else {
1032 /* Most complex -- need to open a gap */
1033 char fbuf[4096];
1034 off_t lastwritten = flength + vlength - length;
1035
1036 /* Start reading exactly the buffer size back from the end. */
1037 fseeko(ff, flength - sizeof(fbuf), SEEK_SET);
1038 while (offset < ftello(ff)) {
1039 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
1040 ast_log(LOG_ERROR, "Short read?!!\n");
1041 fclose(ff);
1042 return -1;
1043 }
1044 /* Since the read moved our file ptr forward, we reverse, but
1045 * seek an offset equal to the amount we want to extend the
1046 * file by */
1047 fseeko(ff, vlength - length - sizeof(fbuf), SEEK_CUR);
1048
1049 /* Note the location of this buffer -- we must not overwrite this position. */
1050 lastwritten = ftello(ff);
1051
1052 if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
1053 ast_log(LOG_ERROR, "Short write?!!\n");
1054 fclose(ff);
1055 return -1;
1056 }
1057
1058 if (lastwritten < offset + sizeof(fbuf)) {
1059 break;
1060 }
1061 /* Our file pointer is now either pointing to the end of the
1062 * file (new position) or a multiple of the fbuf size back from
1063 * that point. Move back to where we want to start reading
1064 * again. We never actually try to read beyond the end of the
1065 * file, so we don't have do deal with short reads, as we would
1066 * when we're shortening the file. */
1067 fseeko(ff, 2 * sizeof(fbuf) + vlength - length, SEEK_CUR);
1068 }
1069
1070 /* Last part of the file that we need to preserve */
1071 if (fseeko(ff, offset + length, SEEK_SET)) {
1072 ast_log(LOG_WARNING, "Unable to seek to %" PRId64 " + %" PRId64 " != %" PRId64 "?)\n", offset, length, ftello(ff));
1073 }
1074
1075 /* Doesn't matter how much we read -- just need to restrict the write */
1076 ast_debug(1, "Reading at %" PRId64 "\n", ftello(ff));
1077 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
1078 ast_log(LOG_ERROR, "Short read?!!\n");
1079 }
1080 fseek(ff, offset, SEEK_SET);
1081 /* Write out the value, then write just up until where we last moved some data */
1082 if (fwrite(value, 1, vlength, ff) < vlength) {
1083 ast_log(LOG_ERROR, "Short write?!!\n");
1084 } else {
1085 off_t curpos = ftello(ff);
1086 foplen = lastwritten - curpos;
1087 if (fwrite(fbuf, 1, foplen, ff) < foplen) {
1088 ast_log(LOG_ERROR, "Short write?!!\n");
1089 }
1090 }
1091 fclose(ff);
1092 }
1093 } else {
1094 enum file_format newline_format = FF_UNKNOWN;
1095
1096 /* Line mode */
1097 if (args.argc == 5) {
1098 if (tolower(args.format[0]) == 'u') {
1099 newline_format = FF_UNIX;
1100 } else if (tolower(args.format[0]) == 'm') {
1101 newline_format = FF_MAC;
1102 } else if (tolower(args.format[0]) == 'd') {
1103 newline_format = FF_DOS;
1104 }
1105 }
1106 if (newline_format == FF_UNKNOWN && (newline_format = file2format(args.filename)) == FF_UNKNOWN) {
1107 ast_log(LOG_ERROR, "File '%s' not in line format\n", args.filename);
1108 return -1;
1109 }
1110
1111 if (strchr(args.options, 'a')) {
1112 /* Append to file */
1113 if (!(ff = fopen(args.filename, "a"))) {
1114 ast_log(LOG_ERROR, "Unable to open '%s' for appending: %s\n", args.filename, strerror(errno));
1115 return -1;
1116 }
1117 if (fwrite(value, 1, vlength, ff) < vlength) {
1118 ast_log(LOG_ERROR, "Short write?!!\n");
1119 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) {
1120 ast_log(LOG_ERROR, "Short write?!!\n");
1121 }
1122 fclose(ff);
1123 } else if (offset == 0 && length == LLONG_MAX) {
1124 /* Overwrite file */
1125 off_t truncsize;
1126 if (!(ff = fopen(args.filename, "w"))) {
1127 ast_log(LOG_ERROR, "Unable to open '%s' for writing: %s\n", args.filename, strerror(errno));
1128 return -1;
1129 }
1130 if (fwrite(value, 1, vlength, ff) < vlength) {
1131 ast_log(LOG_ERROR, "Short write?!!\n");
1132 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) {
1133 ast_log(LOG_ERROR, "Short write?!!\n");
1134 }
1135 if ((truncsize = ftello(ff)) < 0) {
1136 ast_log(AST_LOG_ERROR, "Unable to determine truncate position of '%s': %s\n", args.filename, strerror(errno));
1137 }
1138 fclose(ff);
1139 if (truncsize >= 0 && truncate(args.filename, truncsize)) {
1140 ast_log(LOG_ERROR, "Unable to truncate file '%s': %s\n", args.filename, strerror(errno));
1141 return -1;
1142 }
1143 } else {
1144 int64_t offset_offset = (offset == 0 ? 0 : -1), length_offset = -1, flength, i, current_length = 0;
1145 char dos_state = 0, fbuf[4096];
1146
1147 if (offset < 0 && length < offset) {
1148 /* Nonsense! */
1149 ast_log(LOG_ERROR, "Length cannot specify a position prior to the offset\n");
1150 return -1;
1151 }
1152
1153 if (!(ff = fopen(args.filename, "r+"))) {
1154 ast_log(LOG_ERROR, "Cannot open '%s' for modification: %s\n", args.filename, strerror(errno));
1155 return -1;
1156 }
1157
1158 if (fseek(ff, 0, SEEK_END)) {
1159 ast_log(LOG_ERROR, "Cannot seek to end of file '%s': %s\n", args.filename, strerror(errno));
1160 fclose(ff);
1161 return -1;
1162 }
1163 if ((flength = ftello(ff)) < 0) {
1164 ast_log(AST_LOG_ERROR, "Cannot determine end position of file '%s': %s\n", args.filename, strerror(errno));
1165 fclose(ff);
1166 return -1;
1167 }
1168
1169 /* For negative offset and/or negative length */
1170 if (offset < 0 || length < 0) {
1171 int64_t count = 0;
1172 for (i = (flength / sizeof(fbuf)) * sizeof(fbuf); i >= 0; i -= sizeof(fbuf)) {
1173 char *pos;
1174 if (fseeko(ff, i, SEEK_SET)) {
1175 ast_log(LOG_ERROR, "Cannot seek to offset %" PRId64 ": %s\n", i, strerror(errno));
1176 }
1177 if (i + sizeof(fbuf) >= flength) {
1178 memset(fbuf, 0, sizeof(fbuf));
1179 }
1180 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
1181 ast_log(LOG_ERROR, "Short read: %s\n", strerror(errno));
1182 fclose(ff);
1183 return -1;
1184 }
1185 for (pos = fbuf + sizeof(fbuf) - 1; pos >= fbuf; pos--) {
1186 LINE_COUNTER(pos, newline_format, count);
1187
1188 if (length < 0 && count * -1 == length) {
1189 length_offset = i + (pos - fbuf);
1190 } else if (offset < 0 && count * -1 == (offset - 1)) {
1191 /* Found our initial offset. We're done with reverse motion! */
1192 if (newline_format == FF_DOS) {
1193 offset_offset = i + (pos - fbuf) + 2;
1194 } else {
1195 offset_offset = i + (pos - fbuf) + 1;
1196 }
1197 break;
1198 }
1199 }
1200 if ((offset < 0 && offset_offset >= 0) || (offset >= 0 && length_offset >= 0)) {
1201 break;
1202 }
1203 }
1204 /* We're at the beginning, and the negative offset indicates the exact number of lines in the file */
1205 if (offset < 0 && offset_offset < 0 && offset == count * -1) {
1206 offset_offset = 0;
1207 }
1208 }
1209
1210 /* Positve line offset */
1211 if (offset > 0) {
1212 int64_t count = 0;
1213 fseek(ff, 0, SEEK_SET);
1214 for (i = 0; i < flength; i += sizeof(fbuf)) {
1215 char *pos;
1216 if (i + sizeof(fbuf) >= flength) {
1217 memset(fbuf, 0, sizeof(fbuf));
1218 }
1219 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
1220 ast_log(LOG_ERROR, "Short read?!!\n");
1221 fclose(ff);
1222 return -1;
1223 }
1224 for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) {
1225 LINE_COUNTER(pos, newline_format, count);
1226
1227 if (count == offset) {
1228 offset_offset = i + (pos - fbuf) + 1;
1229 break;
1230 }
1231 }
1232 if (offset_offset >= 0) {
1233 break;
1234 }
1235 }
1236 }
1237
1238 if (offset_offset < 0) {
1239 ast_log(LOG_ERROR, "Offset '%s' refers to before the beginning of the file!\n", args.offset);
1240 fclose(ff);
1241 return -1;
1242 }
1243
1244 if (length == 0) {
1245 length_offset = offset_offset;
1246 } else if (length == LLONG_MAX) {
1247 length_offset = flength;
1248 }
1249
1250 /* Positive line length */
1251 if (length_offset < 0) {
1252 fseeko(ff, offset_offset, SEEK_SET);
1253 for (i = offset_offset; i < flength; i += sizeof(fbuf)) {
1254 char *pos;
1255 if (i + sizeof(fbuf) >= flength) {
1256 memset(fbuf, 0, sizeof(fbuf));
1257 }
1258 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
1259 ast_log(LOG_ERROR, "Short read?!!\n");
1260 fclose(ff);
1261 return -1;
1262 }
1263 for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) {
1264 LINE_COUNTER(pos, newline_format, current_length);
1265
1266 if (current_length == length) {
1267 length_offset = i + (pos - fbuf) + 1;
1268 break;
1269 }
1270 }
1271 if (length_offset >= 0) {
1272 break;
1273 }
1274 }
1275 if (length_offset < 0) {
1276 /* Exceeds length of file */
1277 ast_debug(3, "Exceeds length of file? length=%" PRId64 ", count=%" PRId64 ", flength=%" PRId64 "\n", length, current_length, flength);
1278 length_offset = flength;
1279 }
1280 }
1281
1282 /* Have offset_offset and length_offset now */
1283 if (length_offset - offset_offset == vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format)))) {
1284 /* Simple case - replacement of text inline */
1285 fseeko(ff, offset_offset, SEEK_SET);
1286 if (fwrite(value, 1, vlength, ff) < vlength) {
1287 ast_log(LOG_ERROR, "Short write?!!\n");
1288 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) {
1289 ast_log(LOG_ERROR, "Short write?!!\n");
1290 }
1291 fclose(ff);
1292 } else if (length_offset - offset_offset > vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format)))) {
1293 /* More complex case - need to shorten file */
1294 off_t cur;
1295 int64_t length_length = length_offset - offset_offset;
1296 size_t vlen = vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format)));
1297
1298 ast_debug(3, "offset=%s/%" PRId64 ", length=%s/%" PRId64 " (%" PRId64 "), vlength=%" PRId64 ", flength=%" PRId64 "\n",
1299 args.offset, offset_offset, args.length, length_offset, length_length, vlength, flength);
1300
1301 fseeko(ff, offset_offset, SEEK_SET);
1302 if (fwrite(value, 1, vlength, ff) < vlength) {
1303 ast_log(LOG_ERROR, "Short write?!!\n");
1304 fclose(ff);
1305 return -1;
1306 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, vlen - vlength, ff) < vlen - vlength) {
1307 ast_log(LOG_ERROR, "Short write?!!\n");
1308 fclose(ff);
1309 return -1;
1310 }
1311 while ((cur = ftello(ff)) < flength) {
1312 if (cur < 0) {
1313 ast_log(AST_LOG_ERROR, "Unable to determine last write position for '%s': %s\n", args.filename, strerror(errno));
1314 fclose(ff);
1315 return -1;
1316 }
1317 fseeko(ff, length_length - vlen, SEEK_CUR);
1318 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
1319 ast_log(LOG_ERROR, "Short read?!!\n");
1320 fclose(ff);
1321 return -1;
1322 }
1323 /* Seek to where we last stopped writing */
1324 fseeko(ff, cur, SEEK_SET);
1325 if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
1326 ast_log(LOG_ERROR, "Short write?!!\n");
1327 fclose(ff);
1328 return -1;
1329 }
1330 }
1331 fclose(ff);
1332 if (truncate(args.filename, flength - (length_length - vlen))) {
1333 ast_log(LOG_ERROR, "Truncation of file failed: %s\n", strerror(errno));
1334 }
1335 } else {
1336 /* Most complex case - need to lengthen file */
1337 size_t vlen = vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format)));
1338 int64_t origlen = length_offset - offset_offset;
1339 off_t lastwritten = flength + vlen - origlen;
1340
1341 ast_debug(3, "offset=%s/%" PRId64 ", length=%s/%" PRId64 ", vlength=%" PRId64 ", flength=%" PRId64 "\n",
1342 args.offset, offset_offset, args.length, length_offset, vlength, flength);
1343
1344 fseeko(ff, flength - sizeof(fbuf), SEEK_SET);
1345 while (offset_offset + sizeof(fbuf) < ftello(ff)) {
1346 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
1347 ast_log(LOG_ERROR, "Short read?!!\n");
1348 fclose(ff);
1349 return -1;
1350 }
1351 fseeko(ff, sizeof(fbuf) - vlen - origlen, SEEK_CUR);
1352 if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
1353 ast_log(LOG_ERROR, "Short write?!!\n");
1354 fclose(ff);
1355 return -1;
1356 }
1357 if ((lastwritten = ftello(ff) - sizeof(fbuf)) < offset_offset + sizeof(fbuf)) {
1358 break;
1359 }
1360 fseeko(ff, 2 * sizeof(fbuf) + vlen - origlen, SEEK_CUR);
1361 }
1362 fseek(ff, length_offset, SEEK_SET);
1363 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
1364 ast_log(LOG_ERROR, "Short read?!!\n");
1365 fclose(ff);
1366 return -1;
1367 }
1368 fseek(ff, offset_offset, SEEK_SET);
1369 if (fwrite(value, 1, vlength, ff) < vlength) {
1370 ast_log(LOG_ERROR, "Short write?!!\n");
1371 fclose(ff);
1372 return -1;
1373 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) {
1374 ast_log(LOG_ERROR, "Short write?!!\n");
1375 fclose(ff);
1376 return -1;
1377 } else {
1378 off_t curpos = ftello(ff);
1379 foplen = lastwritten - curpos;
1380 if (fwrite(fbuf, 1, foplen, ff) < foplen) {
1381 ast_log(LOG_ERROR, "Short write?!!\n");
1382 }
1383 }
1384 fclose(ff);
1385 }
1386 }
1387 }
1388
1389 return 0;
1390}
1391
1393 .name = "ENV",
1394 .read = env_read,
1395 .write = env_write
1396};
1397
1399 .name = "STAT",
1400 .read = stat_read,
1401 .read_max = 12,
1402};
1403
1405 .name = "FILE",
1406 .read2 = file_read,
1407 .write = file_write,
1408};
1409
1411 .name = "FILE_COUNT_LINE",
1412 .read2 = file_count_line,
1413 .read_max = 12,
1414};
1415
1417 .name = "FILE_FORMAT",
1418 .read2 = file_format,
1419 .read_max = 2,
1420};
1421
1423 .name = "DIRNAME",
1424 .read = file_dirname,
1425 .read_max = 12,
1426};
1427
1429 .name = "BASENAME",
1430 .read = file_basename,
1431 .read_max = 12,
1432};
1433
1448
1463
1464AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Environment/filesystem dialplan functions");
int unsetenv(const char *name)
int setenv(const char *name, const char *value, int overwrite)
#define LLONG_MAX
char * strsep(char **str, const char *delims)
Asterisk main include file. File version handling, generic pbx functions.
#define ast_log
Definition astobj2.c:42
General Asterisk PBX channel definitions.
char * end
Definition eagi_proxy.c:73
char buf[BUFSIZE]
Definition eagi_proxy.c:66
Generic File Format Support. Should be included by clients of the file handling routines....
static int env_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
Definition func_env.c:386
static struct ast_custom_function file_format_function
Definition func_env.c:1416
static int file_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
Definition func_env.c:905
static struct ast_custom_function file_count_line_function
Definition func_env.c:1410
static int64_t count_lines(const char *filename, enum file_format newline_format)
Definition func_env.c:450
static enum file_format file2format(const char *filename)
Definition func_env.c:564
static int env_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition func_env.c:370
static struct ast_custom_function env_function
Definition func_env.c:1392
static int file_count_line(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
Definition func_env.c:525
const char * format2term(enum file_format f)
Definition func_env.c:899
static int stat_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition func_env.c:400
static struct ast_custom_function file_function
Definition func_env.c:1404
static struct ast_custom_function file_basename_function
Definition func_env.c:1428
static int file_basename(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition func_env.c:628
file_format
Definition func_env.c:443
@ FF_UNKNOWN
Definition func_env.c:444
@ FF_UNIX
Definition func_env.c:445
@ FF_DOS
Definition func_env.c:446
@ FF_MAC
Definition func_env.c:447
static struct ast_custom_function stat_function
Definition func_env.c:1398
static int load_module(void)
Definition func_env.c:1449
static int unload_module(void)
Definition func_env.c:1434
#define LINE_COUNTER(cptr, term, counter)
Definition func_env.c:550
static int file_read(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
Definition func_env.c:645
static struct ast_custom_function file_dirname_function
Definition func_env.c:1422
static int file_dirname(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition func_env.c:611
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define AST_LOG_ERROR
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_WARNING
int errno
Asterisk module definitions.
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition module.h:581
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition module.h:46
Core PBX routines and definitions.
#define ast_custom_function_register_escalating(acf, escalation)
Register a custom function which requires escalated privileges.
Definition pbx.h:1572
#define ast_custom_function_register(acf)
Register a custom function.
Definition pbx.h:1563
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
@ AST_CFE_READ
Definition pbx.h:1555
@ AST_CFE_BOTH
Definition pbx.h:1557
static struct @523 args
#define NULL
Definition resample.c:96
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition strings.h:80
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
char * ast_str_append_substr(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc)
Append a non-NULL terminated substring to the end of a dynamic string.
Definition strings.h:1062
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition strings.h:693
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition strings.h:1113
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition strings.h:425
Main Channel structure associated with a channel.
Data structure associated with a custom dialplan function.
Definition pbx.h:118
const char * name
Definition pbx.h:119
Support for dynamic strings.
Definition strings.h:623
int value
Definition syslog.c:37
static struct test_options options
Utility functions.
#define MIN(a, b)
Definition utils.h:252