From 3bb818df405e667087fe3336cd91159790f63981 Mon Sep 17 00:00:00 2001
From: antirez <antirez@gmail.com>
Date: Tue, 24 May 2011 19:43:11 +0200
Subject: [PATCH] Make sure error and status replies emitted by Lua scripts can
 never have more than a newline, otherwise it is a protocol violation and
 clients will desync.

---
 src/networking.c |  7 +++++++
 src/scripting.c  | 12 ++++++++----
 src/sds.c        | 23 +++++++++++++++++++++++
 src/sds.h        |  1 +
 4 files changed, 39 insertions(+), 4 deletions(-)

diff --git a/src/networking.c b/src/networking.c
index 976b472a..e88e27d1 100644
--- a/src/networking.c
+++ b/src/networking.c
@@ -246,10 +246,17 @@ void addReplyError(redisClient *c, char *err) {
 }
 
 void addReplyErrorFormat(redisClient *c, const char *fmt, ...) {
+    size_t l, j;
     va_list ap;
     va_start(ap,fmt);
     sds s = sdscatvprintf(sdsempty(),fmt,ap);
     va_end(ap);
+    /* Make sure there are no newlines in the string, otherwise invalid protocol
+     * is emitted. */
+    l = sdslen(s);
+    for (j = 0; j < l; j++) {
+        if (s[j] == '\r' || s[j] == '\n') s[j] = ' ';
+    }
     _addReplyError(c,s,sdslen(s));
     sdsfree(s);
 }
diff --git a/src/scripting.c b/src/scripting.c
index be6247a9..469d7870 100644
--- a/src/scripting.c
+++ b/src/scripting.c
@@ -332,8 +332,10 @@ void luaReplyToRedisReply(redisClient *c, lua_State *lua) {
         lua_gettable(lua,-2);
         t = lua_type(lua,-1);
         if (t == LUA_TSTRING) {
-            addReplySds(c,sdscatprintf(sdsempty(),
-                    "-%s\r\n",(char*)lua_tostring(lua,-1)));
+            sds err = sdsnew(lua_tostring(lua,-1));
+            sdsmapchars(err,"\r\n","  ",2);
+            addReplySds(c,sdscatprintf(sdsempty(),"-%s\r\n",err));
+            sdsfree(err);
             lua_pop(lua,2);
             return;
         }
@@ -343,8 +345,10 @@ void luaReplyToRedisReply(redisClient *c, lua_State *lua) {
         lua_gettable(lua,-2);
         t = lua_type(lua,-1);
         if (t == LUA_TSTRING) {
-            addReplySds(c,sdscatprintf(sdsempty(),
-                    "+%s\r\n",(char*)lua_tostring(lua,-1)));
+            sds ok = sdsnew(lua_tostring(lua,-1));
+            sdsmapchars(ok,"\r\n","  ",2);
+            addReplySds(c,sdscatprintf(sdsempty(),"+%s\r\n",ok));
+            sdsfree(ok);
             lua_pop(lua,1);
         } else {
             void *replylen = addDeferredMultiBulkLength(c);
diff --git a/src/sds.c b/src/sds.c
index 63507000..2ec7c3cb 100644
--- a/src/sds.c
+++ b/src/sds.c
@@ -553,6 +553,29 @@ void sdssplitargs_free(sds *argv, int argc) {
     zfree(argv);
 }
 
+/* Modify the string substituting all the occurrences of the set of
+ * characters specifed in the 'from' string to the corresponding character
+ * in the 'to' array.
+ *
+ * For instance: sdsmapchars(mystring, "ho", "01", 2)
+ * will have the effect of turning the string "hello" into "0ell1".
+ *
+ * The function returns the sds string pointer, that is always the same
+ * as the input pointer since no resize is needed. */
+sds sdsmapchars(sds s, char *from, char *to, size_t setlen) {
+    size_t j, i, l = sdslen(s);
+
+    for (j = 0; j < l; j++) {
+        for (i = 0; i < setlen; i++) {
+            if (s[j] == from[i]) {
+                s[j] = to[i];
+                break;
+            }
+        }
+    }
+    return s;
+}
+
 #ifdef SDS_TEST_MAIN
 #include <stdio.h>
 #include "testhelp.h"
diff --git a/src/sds.h b/src/sds.h
index ea43f868..af5c4910 100644
--- a/src/sds.h
+++ b/src/sds.h
@@ -85,5 +85,6 @@ sds sdsfromlonglong(long long value);
 sds sdscatrepr(sds s, char *p, size_t len);
 sds *sdssplitargs(char *line, int *argc);
 void sdssplitargs_free(sds *argv, int argc);
+sds sdsmapchars(sds s, char *from, char *to, size_t setlen);
 
 #endif
-- 
2.47.2