diff --git a/CHANGES b/CHANGES
index 19946ab9d4a4c067b54241729f263ba9b505626e..140b72581116fe45583851c13ad75b0b446f05e8 100644
--- a/CHANGES
+++ b/CHANGES
@@ -339,6 +339,10 @@ AGI (Asterisk Gateway Interface)
    will start the playback of the audio at the position specified. It will
    also return the final position of the file in 'endpos'.
 
+ * The SAY ALPHA command now accepts an additional parameter to control
+   whether it specifies the case of uppercase, lowercase, or all letters to
+   provide functionality similar to SayAlphaCase.
+
 CDR (Call Detail Records)
 ------------------
  * Significant changes have been made to the behavior of CDRs. For a full
diff --git a/apps/app_chanspy.c b/apps/app_chanspy.c
index 0f40e319369ed1a1bbd46a50e7225629d9b5fd89..061fa261d63de8edd2d20d78de188ff49f9a1f17 100644
--- a/apps/app_chanspy.c
+++ b/apps/app_chanspy.c
@@ -1048,7 +1048,7 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
 								break;
 							}
 						} else {
-							res = ast_say_character_str(chan, peer_name, "", ast_channel_language(chan));
+							res = ast_say_character_str(chan, peer_name, "", ast_channel_language(chan), AST_SAY_CASE_NONE);
 						}
 					}
 					if (ptr && (num = atoi(ptr))) {
diff --git a/apps/app_directory.c b/apps/app_directory.c
index edfc349ea7ea6279c628fd13db9fdf129e3f123a..ccdac427b4b3d6acdf98d37e0afadd47518ae727 100644
--- a/apps/app_directory.c
+++ b/apps/app_directory.c
@@ -281,13 +281,13 @@ static int play_mailbox_owner(struct ast_channel *chan, const char *context,
 		/* If Option 'e' was specified, also read the extension number with the name */
 		if (ast_test_flag(flags, OPT_SAYEXTENSION)) {
 			ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
-			res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, ast_channel_language(chan));
+			res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, ast_channel_language(chan), AST_SAY_CASE_NONE);
 		}
 	} else {
-		res = ast_say_character_str(chan, S_OR(name, ext), AST_DIGIT_ANY, ast_channel_language(chan));
+		res = ast_say_character_str(chan, S_OR(name, ext), AST_DIGIT_ANY, ast_channel_language(chan), AST_SAY_CASE_NONE);
 		if (!ast_strlen_zero(name) && ast_test_flag(flags, OPT_SAYEXTENSION)) {
 			ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
-			res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, ast_channel_language(chan));
+			res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, ast_channel_language(chan), AST_SAY_CASE_NONE);
 		}
 	}
 
diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c
index 1f048a283687a15429465cee4e98cdfeab6c0335..13e94a8baeb8ca65490b20e5dfdd470d1f489e6c 100644
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -13683,7 +13683,7 @@ static int vmsayname_exec(struct ast_channel *chan, const char *data)
 		ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
 		res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
 		if (!res) {
-			res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, ast_channel_language(chan));
+			res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, ast_channel_language(chan), AST_SAY_CASE_NONE);
 		}
 	}
 
diff --git a/include/asterisk/say.h b/include/asterisk/say.h
index fac606aa81ec0aad21f69d490e746c371479b9da..93c525dd21c21ac584254b32b6243d7ea56a593a 100644
--- a/include/asterisk/say.h
+++ b/include/asterisk/say.h
@@ -148,13 +148,23 @@ SAY_EXTERN int (* ast_say_digit_str_full)(struct ast_channel *chan, const char *
  */
 SAY_EXTERN int (* ast_say_full)(struct ast_channel *chan, const char *num, const char *ints, const char *lang, const char *options, int audiofd, int ctrlfd) SAY_INIT(ast_say_full);
 
+/*!
+ * \brief Controls how ast_say_character_str denotes the case of characters in a string
+ */
+enum ast_say_case_sensitivity {
+	AST_SAY_CASE_NONE,  /*!< Do not distinguish case on any letters */
+	AST_SAY_CASE_LOWER, /*!< Denote case only on lower case letters, upper case is assumed otherwise */
+	AST_SAY_CASE_UPPER, /*!< Denote case only on upper case letters, lower case is assumed otherwise */
+	AST_SAY_CASE_ALL,   /*!< Denote case on all letters, upper and lower */
+};
+
 /*! \brief
  * function to pronounce character and phonetic strings
  */
 int ast_say_character_str(struct ast_channel *chan, const char *num,
-	const char *ints, const char *lang);
+	const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity);
 
-SAY_EXTERN int (* ast_say_character_str_full)(struct ast_channel *chan, const char *num, const char *ints, const char *lang, int audiofd, int ctrlfd) SAY_INIT(ast_say_character_str_full);
+SAY_EXTERN int (* ast_say_character_str_full)(struct ast_channel *chan, const char *num, const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity, int audiofd, int ctrlfd) SAY_INIT(ast_say_character_str_full);
 
 int ast_say_phonetic_str(struct ast_channel *chan, const char *num,
 	const char *ints, const char *lang);
diff --git a/main/channel.c b/main/channel.c
index c5ff77b86d2e21687ff0918c4faec1ace596932f..5daf5825e8ccc4523c8065c934f6f85c4bfb9ec6 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -7792,9 +7792,9 @@ int ast_say_digit_str(struct ast_channel *chan, const char *str,
 }
 
 int ast_say_character_str(struct ast_channel *chan, const char *str,
-	const char *ints, const char *lang)
+	const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity)
 {
-	return ast_say_character_str_full(chan, str, ints, lang, -1, -1);
+	return ast_say_character_str_full(chan, str, ints, lang, sensitivity, -1, -1);
 }
 
 int ast_say_phonetic_str(struct ast_channel *chan, const char *str,
diff --git a/main/pbx.c b/main/pbx.c
index d5a46a5fdedfb339a7afa57d3dd572c3f493f06c..27a586ef91001f6fbb034a5a9bb849bd2b450fc7 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -501,6 +501,46 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 			<ref type="function">CHANNEL</ref>
 		</see-also>
 	</application>
+	<application name="SayAlphaCase" language="en_US">
+		<synopsis>
+			Say Alpha.
+		</synopsis>
+		<syntax>
+			<parameter name="casetype" required="true" >
+				<enumlist>
+					<enum name="a">
+						<para>Case sensitive (all) pronunciation.
+						(Ex: SayAlphaCase(a,aBc); - lowercase a uppercase b lowercase c).</para>
+					</enum>
+					<enum name="l">
+						<para>Case sensitive (lower) pronunciation.
+						(Ex: SayAlphaCase(l,aBc); - lowercase a b lowercase c).</para>
+					</enum>
+					<enum name="n">
+						<para>Case insensitive pronunciation. Equivalent to SayAlpha.
+						(Ex: SayAlphaCase(n,aBc) - a b c).</para>
+					</enum>
+					<enum name="u">
+						<para>Case sensitive (upper) pronunciation.
+						(Ex: SayAlphaCase(u,aBc); - a uppercase b c).</para>
+					</enum>
+				</enumlist>
+			</parameter>
+			<parameter name="string" required="true" />
+		</syntax>
+		<description>
+			<para>This application will play the sounds that correspond to the letters of the
+			given <replaceable>string</replaceable>.  Optionally, a <replaceable>casetype</replaceable> may be
+			specified.  This will be used for case-insensitive or case-sensitive pronunciations.</para>
+		</description>
+		<see-also>
+			<ref type="application">SayDigits</ref>
+			<ref type="application">SayNumber</ref>
+			<ref type="application">SayPhonetic</ref>
+			<ref type="application">SayAlpha</ref>
+			<ref type="function">CHANNEL</ref>
+		</see-also>
+	</application>
 	<application name="SayDigits" language="en_US">
 		<synopsis>
 			Say Digits.
@@ -1122,6 +1162,7 @@ static int pbx_builtin_execiftime(struct ast_channel *, const char *);
 static int pbx_builtin_saynumber(struct ast_channel *, const char *);
 static int pbx_builtin_saydigits(struct ast_channel *, const char *);
 static int pbx_builtin_saycharacters(struct ast_channel *, const char *);
+static int pbx_builtin_saycharacters_case(struct ast_channel *, const char *);
 static int pbx_builtin_sayphonetic(struct ast_channel *, const char *);
 static int matchcid(const char *cidpattern, const char *callerid);
 #ifdef NEED_DEBUG
@@ -1297,6 +1338,7 @@ static struct pbx_builtin {
 	{ "RaiseException", pbx_builtin_raise_exception },
 	{ "Ringing",        pbx_builtin_ringing },
 	{ "SayAlpha",       pbx_builtin_saycharacters },
+	{ "SayAlphaCase",   pbx_builtin_saycharacters_case },
 	{ "SayDigits",      pbx_builtin_saydigits },
 	{ "SayNumber",      pbx_builtin_saynumber },
 	{ "SayPhonetic",    pbx_builtin_sayphonetic },
@@ -11260,12 +11302,60 @@ static int pbx_builtin_saydigits(struct ast_channel *chan, const char *data)
 	return res;
 }
 
+static int pbx_builtin_saycharacters_case(struct ast_channel *chan, const char *data)
+{
+	int res = 0;
+	int sensitivity = 0;
+	char *parse;
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(options);
+		AST_APP_ARG(characters);
+	);
+
+	if (ast_strlen_zero(data)) {
+		ast_log(LOG_WARNING, "SayAlphaCase requires two arguments (options, characters)\n");
+		return 0;
+	}
+
+	parse = ast_strdupa(data);
+	AST_STANDARD_APP_ARGS(args, parse);
+
+	if (!args.options || strlen(args.options) != 1) {
+		ast_log(LOG_WARNING, "SayAlphaCase options are mutually exclusive and required\n");
+		return 0;
+	}
+
+	switch (args.options[0]) {
+	case 'a':
+		sensitivity = AST_SAY_CASE_ALL;
+		break;
+	case 'l':
+		sensitivity = AST_SAY_CASE_LOWER;
+		break;
+	case 'n':
+		sensitivity = AST_SAY_CASE_NONE;
+		break;
+	case 'u':
+		sensitivity = AST_SAY_CASE_UPPER;
+		break;
+	default:
+		ast_log(LOG_WARNING, "Invalid option: '%s'\n", args.options);
+		return 0;
+	}
+
+	res = ast_say_character_str(chan, args.characters, "", ast_channel_language(chan), sensitivity);
+
+	return res;
+}
+
 static int pbx_builtin_saycharacters(struct ast_channel *chan, const char *data)
 {
 	int res = 0;
 
-	if (data)
-		res = ast_say_character_str(chan, data, "", ast_channel_language(chan));
+	if (data) {
+		res = ast_say_character_str(chan, data, "", ast_channel_language(chan), AST_SAY_CASE_NONE);
+	}
+
 	return res;
 }
 
diff --git a/main/say.c b/main/say.c
index 295d4110f4639997620f536edc9069d3e49ed348..ed0a7b52417b6263fb5443ac116f62d0924e88d6 100644
--- a/main/say.c
+++ b/main/say.c
@@ -61,13 +61,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
 
 
-static int say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
+static int say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity, int audiofd, int ctrlfd)
 {
 	const char *fn;
 	char fnbuf[10], asciibuf[20] = "letters/ascii";
 	char ltr;
 	int num = 0;
 	int res = 0;
+	int upper = 0;
+	int lower = 0;
 
 	while (str[num] && !res) {
 		fn = NULL;
@@ -121,9 +123,35 @@ static int say_character_str_full(struct ast_channel *chan, const char *str, con
 			break;
 		default:
 			ltr = str[num];
-			if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A';		/* file names are all lower-case */
-			strcpy(fnbuf, "letters/X");
-			fnbuf[8] = ltr;
+			if ('A' <= ltr && ltr <= 'Z') {
+				ltr += 'a' - 'A';		/* file names are all lower-case */
+				switch (sensitivity) {
+				case AST_SAY_CASE_UPPER:
+				case AST_SAY_CASE_ALL:
+					upper = !upper;
+				case AST_SAY_CASE_LOWER:
+				case AST_SAY_CASE_NONE:
+					break;
+				}
+			} else if ('a' <= ltr && ltr <= 'z') {
+				switch (sensitivity) {
+				case AST_SAY_CASE_LOWER:
+				case AST_SAY_CASE_ALL:
+					lower = !lower;
+				case AST_SAY_CASE_UPPER:
+				case AST_SAY_CASE_NONE:
+					break;
+				}
+			}
+
+			if (upper) {
+				strcpy(fnbuf, "uppercase");
+			} else if (lower) {
+				strcpy(fnbuf, "lowercase");
+			} else {
+				strcpy(fnbuf, "letters/X");
+				fnbuf[8] = ltr;
+			}
 			fn = fnbuf;
 		}
 		if ((fn && ast_fileexists(fn, NULL, lang) > 0) ||
@@ -137,6 +165,9 @@ static int say_character_str_full(struct ast_channel *chan, const char *str, con
 			}
 			ast_stopstream(chan);
 		}
+		if (upper || lower) {
+			continue;
+		}
 		num++;
 	}
 
diff --git a/res/res_agi.c b/res/res_agi.c
index 6619183fc0fae1bbd5cbcab7f0e6c5f73a09d136..74d665bd5018fdb9b1ae2cca5f2043a9d9223672 100644
--- a/res/res_agi.c
+++ b/res/res_agi.c
@@ -2313,11 +2313,37 @@ static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, const
 static int handle_sayalpha(struct ast_channel *chan, AGI *agi, int argc, const char * const argv[])
 {
 	int res;
+	int sensitivity = AST_SAY_CASE_NONE;
 
-	if (argc != 4)
+	if (argc < 4 || argc > 5) {
 		return RESULT_SHOWUSAGE;
+	}
 
-	res = ast_say_character_str_full(chan, argv[2], argv[3], ast_channel_language(chan), agi->audio, agi->ctrl);
+	if (argc > 4) {
+		switch (argv[4][0]) {
+		case 'a':
+		case 'A':
+			sensitivity = AST_SAY_CASE_ALL;
+			break;
+		case 'l':
+		case 'L':
+			sensitivity = AST_SAY_CASE_LOWER;
+			break;
+		case 'n':
+		case 'N':
+			sensitivity = AST_SAY_CASE_NONE;
+			break;
+		case 'u':
+		case 'U':
+			sensitivity = AST_SAY_CASE_UPPER;
+			break;
+		case '\0':
+			break;
+		default:
+			return RESULT_SHOWUSAGE;
+		}
+	}
+	res = ast_say_character_str_full(chan, argv[2], argv[3], ast_channel_language(chan), sensitivity, agi->audio, agi->ctrl);
 	if (res == 1) /* New command */
 		return RESULT_SUCCESS;
 	ast_agi_send(agi->fd, chan, "200 result=%d\n", res);