From 96a699c06525f25edc65bc56a47fa66c6d3a91c0 Mon Sep 17 00:00:00 2001
From: Tilghman Lesher <tilghman@meg.abyt.es>
Date: Mon, 16 Mar 2009 17:33:38 +0000
Subject: [PATCH] Fix an off-by-one error in the FILE() function, and extend
 FILE()'s length parameter to work like variable substitution. Previously,
 FILE() returned one less character than specified, due to the terminating
 NULL.  Both the offset and length parameters now behave identically to the
 way variable substitution offsets and lengths also work. (closes issue
 #14670)  Reported by: BMC

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@182278 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 funcs/func_env.c | 62 ++++++++++++++++++++++++++++++++----------------
 1 file changed, 41 insertions(+), 21 deletions(-)

diff --git a/funcs/func_env.c b/funcs/func_env.c
index c56c50d714..707e96a12f 100644
--- a/funcs/func_env.c
+++ b/funcs/func_env.c
@@ -74,11 +74,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 		<syntax>
 			<parameter name="filename" required="true" />
 			<parameter name="offset" required="true">
-				<para>Maybe specified as any number. if negative <replaceable>offset</replaceable> specifies the number
+				<para>Maybe specified as any number. If negative, <replaceable>offset</replaceable> specifies the number
 				of bytes back from the end of the file.</para>
 			</parameter>
 			<parameter name="length" required="true">
-				<para>If specified, will limit the length of the data read to that size.</para>
+				<para>If specified, will limit the length of the data read to that size. If negative,
+				trims <replaceable>length</replaceable> bytes from the end of the file.</para>
 			</parameter>
 		</syntax>
 		<description>
@@ -166,40 +167,59 @@ static int file_read(struct ast_channel *chan, const char *cmd, char *data, char
 		AST_APP_ARG(offset);
 		AST_APP_ARG(length);
 	);
-	int offset = 0, length;
+	int offset = 0, length, res = 0;
 	char *contents;
+	size_t contents_len;
 
 	AST_STANDARD_APP_ARGS(args, data);
-	if (args.argc > 1)
+	if (args.argc > 1) {
 		offset = atoi(args.offset);
+	}
 
 	if (args.argc > 2) {
-		if ((length = atoi(args.length)) < 1) {
-			ast_log(LOG_WARNING, "Invalid length '%s'.  Returning the max (%d)\n", args.length, (int)len);
-			length = len;
-		} else if (length > len) {
-			ast_log(LOG_WARNING, "Length %d is greater than the max (%d).  Truncating output.\n", length, (int)len);
+		/* The +1/-1 in this code section is to accomodate for the terminating NULL. */
+		if ((length = atoi(args.length) + 1) > len) {
+			ast_log(LOG_WARNING, "Length %d is greater than the max (%d).  Truncating output.\n", length - 1, (int)len - 1);
 			length = len;
 		}
-	} else
+	} else {
 		length = len;
+	}
 
-	if (!(contents = ast_read_textfile(args.filename)))
+	if (!(contents = ast_read_textfile(args.filename))) {
 		return -1;
+	}
 
-	if (offset >= 0)
-		ast_copy_string(buf, &contents[offset], length);
-	else {
-		size_t tmp = strlen(contents);
-		if (offset * -1 > tmp) {
-			ast_log(LOG_WARNING, "Offset is larger than the file size.\n");
-			offset = tmp * -1;
+	do {
+		contents_len = strlen(contents);
+		if (offset > contents_len) {
+			res = -1;
+			break;
 		}
-		ast_copy_string(buf, &contents[tmp + offset], length);
-	}
+
+		if (offset >= 0) {
+			if (length < 0) {
+				if (contents_len - offset + length < 0) {
+					/* Nothing left after trimming */
+					res = -1;
+					break;
+				}
+				ast_copy_string(buf, &contents[offset], contents_len + length);
+			} else {
+				ast_copy_string(buf, &contents[offset], length);
+			}
+		} else {
+			if (offset * -1 > contents_len) {
+				ast_log(LOG_WARNING, "Offset is larger than the file size.\n");
+				offset = contents_len * -1;
+			}
+			ast_copy_string(buf, &contents[contents_len + offset], length);
+		}
+	} while (0);
+
 	ast_free(contents);
 
-	return 0;
+	return res;
 }
 
 static struct ast_custom_function env_function = {
-- 
GitLab