TBR: gzip the content of the messages

Compress the content of the messages before encrypting them, and
decompress them after decrypting them.

Change-Id: I040450c4b2fa3d92949846d06505fd4786f5fa7f
diff --git a/internal/util.go b/internal/util.go
index 104faf5..2048a09 100644
--- a/internal/util.go
+++ b/internal/util.go
@@ -15,10 +15,6 @@
 	"messenger/ifc"
 )
 
-func NewMessageId() string {
-	return uuid.New()
-}
-
 // NewMessageFromFile creates a new Message for the content of the given file.
 // The Message's Id, CreationTime, Length, and Sha256 fields are populated.
 func NewMessageFromFile(file string) (ifc.Message, error) {
@@ -44,7 +40,7 @@
 		}
 	}
 	return ifc.Message{
-		Id:           NewMessageId(),
+		Id:           uuid.New(),
 		CreationTime: time.Now(),
 		Length:       size,
 		Sha256:       h.Sum(nil),
@@ -57,7 +53,7 @@
 	h := sha256.New()
 	h.Write(b)
 	return ifc.Message{
-		Id:           NewMessageId(),
+		Id:           uuid.New(),
 		CreationTime: time.Now(),
 		Length:       int64(len(b)),
 		Sha256:       h.Sum(nil),
diff --git a/vmsg/chat.go b/vmsg/chat.go
index a07ab6e..f9d48b7 100644
--- a/vmsg/chat.go
+++ b/vmsg/chat.go
@@ -8,6 +8,8 @@
 package main
 
 import (
+	"compress/gzip"
+	"errors"
 	"fmt"
 	"io"
 	"io/ioutil"
@@ -255,7 +257,7 @@
 	if err != nil {
 		return
 	}
-	msgText, filename, err := decryptChatMessage(msg.Id, r, incomingDir)
+	msgText, filename, err := decryptChatMessage(r, incomingDir)
 	r.Close()
 	if err != nil {
 		a.printErrorf("*** Unable to handle message: %v\n", err)
@@ -310,8 +312,7 @@
 func (m messages) Swap(i, j int)      { m[i], m[j] = m[j], m[i] }
 
 func sendMessage(ctx *context.T, ps *internal.PubSub, store internal.MessengerStorage, txt, fname string) error {
-	msgId := internal.NewMessageId()
-	encryptedFile, err := encryptChatMessage(msgId, txt, fname)
+	encryptedFile, err := encryptChatMessage(txt, fname)
 	if err != nil {
 		return err
 	}
@@ -322,7 +323,6 @@
 	if err != nil {
 		return err
 	}
-	msg.Id = msgId
 	msg.SenderBlessings, _ = p.BlessingStore().Default()
 	msg.Lifespan = 15 * time.Minute
 	var expiry time.Time
@@ -357,7 +357,7 @@
 	return nil
 }
 
-func encryptChatMessage(id, text, attachment string) (string, error) {
+func encryptChatMessage(text, attachment string) (string, error) {
 	tmpfile, err := ioutil.TempFile("", "vmsg-encrypt-")
 	if err != nil {
 		return "", err
@@ -367,10 +367,12 @@
 	if err != nil {
 		return "", err
 	}
-	w := multipart.NewWriter(enc)
-	if err := w.SetBoundary(id); err != nil {
+	comp, err := gzip.NewWriterLevel(enc, compressionLevel)
+	if err != nil {
 		return "", err
 	}
+	w := multipart.NewWriter(comp)
+	comp.Header.Extra = []byte(w.Boundary())
 
 	// Write text field.
 	if err := w.WriteField("text", text); err != nil {
@@ -394,15 +396,26 @@
 	if err := w.Close(); err != nil {
 		return "", err
 	}
+	if err := comp.Close(); err != nil {
+		return "", err
+	}
 	return tmpfile.Name(), nil
 }
 
-func decryptChatMessage(id string, msgReader io.Reader, dir string) (text, filename string, err error) {
+func decryptChatMessage(msgReader io.Reader, dir string) (text, filename string, err error) {
 	dec, err := aesDecoder(encryptionKey, msgReader)
 	if err != nil {
 		return "", "", err
 	}
-	r := multipart.NewReader(dec, id)
+	decomp, err := gzip.NewReader(dec)
+	if err != nil {
+		if err == gzip.ErrHeader {
+			return "", "", errors.New("incorrect key")
+		}
+		return "", "", err
+	}
+	defer decomp.Close()
+	r := multipart.NewReader(decomp, string(decomp.Header.Extra))
 	form, err := r.ReadForm(1 << 20)
 	if err != nil {
 		return "", "", err
diff --git a/vmsg/main.go b/vmsg/main.go
index bae7339..97f8559 100644
--- a/vmsg/main.go
+++ b/vmsg/main.go
@@ -52,7 +52,8 @@
 	rateAclSenderJson string
 	encryptionKey     string
 
-	incomingDir string
+	incomingDir      string
+	compressionLevel int
 )
 
 func main() {
@@ -74,6 +75,7 @@
 	cmdRoot.Flags.StringVar(&encryptionKey, "encryption-key", defaultEncryptionKey, "Messages are encrypted with AES256 using this key")
 
 	cmdChat.Flags.StringVar(&incomingDir, "incoming-dir", os.TempDir(), "The directory where to save incoming files")
+	cmdChat.Flags.IntVar(&compressionLevel, "compression-level", -1, "The gzip compression level for the message content. A value of -1 means default compressions; 1 is best speed; 9 is best compression.")
 
 	cmdline.HideGlobalFlagsExcept()
 	cmdline.Main(cmdRoot)
diff --git a/vmsg/robot.go b/vmsg/robot.go
index 0aa5477..331d89d 100644
--- a/vmsg/robot.go
+++ b/vmsg/robot.go
@@ -85,7 +85,7 @@
 			if err != nil {
 				continue
 			}
-			msgText, filename, err := decryptChatMessage(msg.Id, r, incomingDir)
+			msgText, filename, err := decryptChatMessage(r, incomingDir)
 			r.Close()
 			if err != nil {
 				ctx.Infof("decryptChatMessage failed: %v", err)