internal static BlobStoreWriter BlobStoreWriterForBody(InputStream body, Database database) { BlobStoreWriter writer = database.GetAttachmentWriter(); writer.Read(body); writer.Finish(); return(writer); }
/// <exception cref="System.Exception"></exception> public virtual void TestBlobStoreWriterForBody() { BlobStore attachments = database.GetAttachments(); InputStream attachmentStream = GetAsset("attachment.png"); BlobStoreWriter blobStoreWriter = Attachment.BlobStoreWriterForBody(attachmentStream , database); string sha1DigestKey = blobStoreWriter.SHA1DigestString(); NUnit.Framework.Assert.IsTrue(sha1DigestKey.Contains("LmsoqJJ6LOn4YS60pYnvrKbBd64=" )); }
/// <exception cref="System.Exception"></exception> public virtual void TestBasicOperation() { BlobStore attachments = database.GetAttachments(); InputStream attachmentStream = GetAsset("attachment.png"); byte[] bytes = IOUtils.ToByteArray(attachmentStream); BlobStoreWriter blobStoreWriter = new BlobStoreWriter(attachments); blobStoreWriter.AppendData(bytes); blobStoreWriter.Finish(); blobStoreWriter.Install(); string sha1DigestKey = blobStoreWriter.SHA1DigestString(); BlobKey keyFromSha1 = new BlobKey(sha1DigestKey); NUnit.Framework.Assert.IsTrue(attachments.GetSizeOfBlob(keyFromSha1) == bytes.Length ); }
public void TestBlobStoreWriter() { var writer = new BlobStoreWriter(_store); writer.AppendData(Encoding.UTF8.GetBytes("part 1, ")); writer.AppendData(Encoding.UTF8.GetBytes("part 2, ")); writer.AppendData(Encoding.UTF8.GetBytes("part 3")); writer.Finish(); writer.Install(); var expectedData = Encoding.UTF8.GetBytes("part 1, part 2, part 3"); var readItem = _store.BlobForKey(writer.GetBlobKey()); readItem.Should().Equal(expectedData, "because the writer should correctly write contents to disk"); Verify(writer.GetBlobKey(), expectedData); }
public void TestStreamAttachmentBlobStoreWriter() { var attachments = database.Attachments; var blobWriter = new BlobStoreWriter(attachments); var testBlob = "foo"; blobWriter.AppendData(Runtime.GetBytesForString(testBlob)); blobWriter.Finish(); var sha1Base64Digest = "sha1-C+7Hteo/D9vJXQ3UfzxbwnXaijM="; Assert.AreEqual(blobWriter.SHA1DigestString(), sha1Base64Digest); Assert.AreEqual(blobWriter.MD5DigestString(), "md5-rL0Y20zC+Fzt72VPzMSk2A=="); // install it blobWriter.Install(); // look it up in blob store and make sure it's there var blobKey = new BlobKey(sha1Base64Digest); var blob = attachments.BlobForKey(blobKey); Assert.IsTrue(Arrays.Equals(Runtime.GetBytesForString(testBlob).ToArray(), blob)); }
public void TestBasicOperation() { var attachmentStream = (InputStream)GetAsset("attachment.png"); var memoryStream = new MemoryStream(); attachmentStream.Wrapped.CopyTo(memoryStream); var bytes = memoryStream.ToArray(); var attachments = database.Attachments; var blobStoreWriter = new BlobStoreWriter(attachments); blobStoreWriter.AppendData(bytes); blobStoreWriter.Finish(); blobStoreWriter.Install(); var sha1DigestKey = blobStoreWriter.SHA1DigestString(); Assert.IsTrue(sha1DigestKey.Contains("LmsoqJJ6LOn4YS60pYnvrKbBd64=")); var keyFromSha1 = new BlobKey(sha1DigestKey); Assert.IsTrue(attachments.GetSizeOfBlob(keyFromSha1) == bytes.Length); }
internal static IDictionary <string, object> InstallAttachmentBodies(IDictionary <string , object> attachments, Database database) { IDictionary <string, object> updatedAttachments = new Dictionary <string, object>(); foreach (string name in attachments.Keys) { object value = attachments.Get(name); if (value is Couchbase.Lite.Attachment) { Couchbase.Lite.Attachment attachment = (Couchbase.Lite.Attachment)value; IDictionary <string, object> metadataMutable = new Dictionary <string, object>(); metadataMutable.PutAll(attachment.GetMetadata()); InputStream body = attachment.GetBodyIfNew(); if (body != null) { // Copy attachment body into the database's blob store: BlobStoreWriter writer = BlobStoreWriterForBody(body, database); metadataMutable.Put("length", (long)writer.GetLength()); metadataMutable.Put("digest", writer.MD5DigestString()); metadataMutable.Put("follows", true); database.RememberAttachmentWriter(writer); } updatedAttachments.Put(name, metadataMutable); } else { if (value is AttachmentInternal) { throw new ArgumentException("AttachmentInternal objects not expected here. Could indicate a bug" ); } else { if (value != null) { updatedAttachments.Put(name, value); } } } } return(updatedAttachments); }
public void TestStreamAttachmentBlobStoreWriter() { var attachments = database.Attachments; var blobWriter = new BlobStoreWriter(attachments); var testBlob = "foo"; blobWriter.AppendData(Encoding.UTF8.GetBytes(testBlob)); blobWriter.Finish(); var sha1Base64Digest = "sha1-C+7Hteo/D9vJXQ3UfzxbwnXaijM="; Assert.AreEqual(blobWriter.SHA1DigestString(), sha1Base64Digest); // install it blobWriter.Install(); // look it up in blob store and make sure it's there var blobKey = new BlobKey(sha1Base64Digest); var blob = attachments.BlobForKey(blobKey); CollectionAssert.AreEqual(Encoding.UTF8.GetBytes(testBlob).ToArray(), blob); }
public virtual void TestStreamAttachmentBlobStoreWriter() { BlobStore attachments = database.GetAttachments(); BlobStoreWriter blobWriter = new BlobStoreWriter(attachments); string testBlob = "foo"; blobWriter.AppendData(Sharpen.Runtime.GetBytesForString(new string(testBlob))); blobWriter.Finish(); string sha1Base64Digest = "sha1-C+7Hteo/D9vJXQ3UfzxbwnXaijM="; NUnit.Framework.Assert.AreEqual(blobWriter.SHA1DigestString(), sha1Base64Digest); NUnit.Framework.Assert.AreEqual(blobWriter.MD5DigestString(), "md5-rL0Y20zC+Fzt72VPzMSk2A==" ); // install it blobWriter.Install(); // look it up in blob store and make sure it's there BlobKey blobKey = new BlobKey(sha1Base64Digest); byte[] blob = attachments.BlobForKey(blobKey); NUnit.Framework.Assert.IsTrue(Arrays.Equals(Sharpen.Runtime.GetBytesForString(testBlob , Sharpen.Extensions.GetEncoding("UTF-8")), blob)); }
public void TestBasicOperation() { var attachmentStream = GetAsset("attachment.png"); var memoryStream = new MemoryStream(); attachmentStream.CopyTo(memoryStream); var bytes = memoryStream.ToArray(); var attachments = database.Attachments; var blobStoreWriter = new BlobStoreWriter(attachments); blobStoreWriter.AppendData(bytes); blobStoreWriter.Finish(); blobStoreWriter.Install(); var sha1DigestKey = blobStoreWriter.SHA1DigestString(); Assert.IsTrue(sha1DigestKey.Contains("LmsoqJJ6LOn4YS60pYnvrKbBd64=")); var keyFromSha1 = new BlobKey(sha1DigestKey); Assert.IsTrue(attachments.GetSizeOfBlob(keyFromSha1) == bytes.Length); }
internal void RememberAttachmentWriter (BlobStoreWriter writer) { var digest = writer.SHA1DigestString(); PendingAttachmentsByDigest[digest] = writer; }
/// <summary>Updates or deletes an attachment, creating a new document revision in the process. /// </summary> /// <remarks> /// Updates or deletes an attachment, creating a new document revision in the process. /// Used by the PUT / DELETE methods called on attachment URLs. /// </remarks> /// <exclude></exclude> /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> internal RevisionInternal UpdateAttachment(string filename, BlobStoreWriter body, string contentType, AttachmentEncoding encoding, string docID, string oldRevID) { var isSuccessful = false; if (String.IsNullOrEmpty (filename) || (body != null && contentType == null) || (oldRevID != null && docID == null) || (body != null && docID == null)) { throw new CouchbaseLiteException(StatusCode.BadRequest); } BeginTransaction(); try { var oldRev = new RevisionInternal(docID, oldRevID, false, this); if (oldRevID != null) { // Load existing revision if this is a replacement: try { LoadRevisionBody(oldRev, DocumentContentOptions.None); } catch (CouchbaseLiteException e) { if (e.GetCBLStatus().GetCode() == StatusCode.NotFound && ExistsDocumentWithIDAndRev(docID, null)) { throw new CouchbaseLiteException(StatusCode.Conflict); } } } else { // If this creates a new doc, it needs a body: oldRev.SetBody(new Body(new Dictionary<string, object>())); } // Update the _attachments dictionary: var oldRevProps = oldRev.GetProperties(); IDictionary<string, object> attachments = null; if (oldRevProps != null) { attachments = (IDictionary<string, object>)oldRevProps.Get("_attachments"); } if (attachments == null) { attachments = new Dictionary<string, object>(); } if (body != null) { var key = body.GetBlobKey(); var digest = key.Base64Digest(); var blobsByDigest = new Dictionary<string, BlobStoreWriter>(); blobsByDigest.Put(digest, body); RememberAttachmentWritersForDigests(blobsByDigest); var encodingName = (encoding == AttachmentEncoding.AttachmentEncodingGZIP) ? "gzip" : null; var dict = new Dictionary<string, object>(); dict.Put("digest", digest); dict.Put("length", body.GetLength()); dict.Put("follows", true); dict.Put("content_type", contentType); dict.Put("encoding", encodingName); attachments.Put(filename, dict); } else { if (oldRevID != null && !attachments.ContainsKey(filename)) { throw new CouchbaseLiteException(StatusCode.NotFound); } attachments.Remove(filename); } var properties = oldRev.GetProperties(); properties.Put("_attachments", attachments); oldRev.SetProperties(properties); // Create a new revision: var putStatus = new Status(); var newRev = PutRevision(oldRev, oldRevID, false, putStatus); isSuccessful = true; return newRev; } catch (SQLException e) { Log.E(Tag, "Error updating attachment", e); throw new CouchbaseLiteException(StatusCode.InternalServerError); } finally { EndTransaction(isSuccessful); } }
internal void RememberAttachmentWriter(BlobStoreWriter writer, string digest) { PendingAttachmentsByDigest[digest] = writer; }
public void FinishedPart() { if (jsonBuffer != null) { ParseJsonBuffer(); } else { curAttachment.Finish(); String sha1String = curAttachment.SHA1DigestString(); attachmentsBySHA1Digest.Put(sha1String, curAttachment); curAttachment = null; } }
public void StartedPart(IDictionary<String, String> headers) { if (document == null) { StartJsonBuffer(headers); } else { Log.V(TAG, " Starting attachment #{0}...", attachmentsBySHA1Digest.Count + 1); curAttachment = database.AttachmentWriter; if (curAttachment == null) { const string msg = "Cannot create blob store writer for the attachment"; Log.W(TAG, msg); throw new CouchbaseLiteException(msg, StatusCode.AttachmentError); } var name = default(string); var contentDisposition = headers.Get("Content-Disposition"); if (contentDisposition != null && contentDisposition.StartsWith("attachment; filename=")) { // TODO: Parse this less simplistically. Right now it assumes it's in exactly the same // format generated by -[CBL_Pusher uploadMultipartRevision:]. CouchDB (as of 1.2) doesn't // output any headers at all on attachments so there's no compatibility issue yet. var contentDispositionUnquoted = Misc.UnquoteString(contentDisposition); name = contentDispositionUnquoted.Substring(21); if (name != null) { attachmentsByName.Put(name, curAttachment); } } var contentEncoding = headers.Get("Content-Encoding"); if (contentEncoding == "gzip") { if (name != null) { try { var attachEncoding = document.GetCast<IDictionary<string, object>>("_attachments"). GetCast <IDictionary<string, object>>(name).GetCast<string>("encoding"); if (attachEncoding != "gzip") { Log.W(TAG, "Attachment '{0}' MIME body is gzipped but attachment isn't", name); throw new CouchbaseLiteException(StatusCode.UnsupportedType); } } catch (NullReferenceException) { throw new CouchbaseLiteException(StatusCode.UnsupportedType); } } } else if (contentEncoding != null) { Log.W(TAG, "Received unsupported Content-Encoding '{0}'", contentEncoding); throw new CouchbaseLiteException(StatusCode.UnsupportedType); } } }
public AtomicAction ActionToChangeEncryptionKey(SymmetricKey newKey) { var action = new AtomicAction(); // Find all the blob files: var blobs = default(string[]); var oldKey = EncryptionKey; blobs = Directory.GetFiles(_path, "*" + FileExtension); if (blobs.Length == 0) { // No blobs, so nothing to encrypt. Just add/remove the encryption marker file: action.AddLogic(() => { Log.To.NoDomain.D(TAG, "{0} {1}", (newKey != null) ? "encrypting" : "decrypting", _path); Log.To.NoDomain.D(TAG, " No blobs to copy; done."); EncryptionKey = newKey; MarkEncrypted(newKey != null); }, () => { EncryptionKey = oldKey; MarkEncrypted(oldKey != null); }, null); return action; } // Create a new directory for the new blob store. Have to do this now, before starting the // action, because farther down we create an action to move it... var tempPath = Path.Combine(Path.GetTempPath(), String.Format("CouchbaseLite-Temp-{0}", Misc.CreateGUID())); action.AddLogic(() => { Log.To.NoDomain.D(TAG, "{0} {1}", (newKey != null) ? "encrypting" : "decrypting", _path); Directory.CreateDirectory(tempPath); }, () => Directory.Delete(tempPath, true), null); var tempStore = default(BlobStore); action.AddLogic(() => { tempStore = new BlobStore(tempPath, newKey); tempStore.MarkEncrypted(true); }, null, null); // Copy each of my blobs into the new store (which will update its encryption): action.AddLogic(() => { foreach(var blobName in blobs) { // Copy file by reading with old key and writing with new one: Log.To.NoDomain.D(TAG, " Copying {0}", blobName); Stream readStream = File.Open(blobName, FileMode.Open, FileAccess.Read, FileShare.Read); if(EncryptionKey != null) { readStream = EncryptionKey.DecryptStream(readStream); } var writer = new BlobStoreWriter(tempStore); try { writer.Read(readStream); writer.Finish(); writer.Install(); } catch(Exception) { writer.Cancel(); throw; } finally { readStream.Dispose(); } } }, null, null); // Replace the attachment dir with the new one: action.AddLogic(AtomicAction.MoveDirectory(tempPath, _path)); // Finally update EncryptionKey: action.AddLogic(() => { EncryptionKey = newKey; }, () => { EncryptionKey = oldKey; }, null); return action; }
public virtual void TestPutAttachment() { const string testAttachmentName = "test_attachment"; var attachments = database.Attachments; attachments.DeleteBlobs(); Assert.AreEqual(0, attachments.Count()); // Put a revision that includes an _attachments dict: var attach1 = Encoding.UTF8.GetBytes("This is the body of attach1"); var base64 = Convert.ToBase64String(attach1); var attachment = new Dictionary<string, object>(); attachment["content_type"] = "text/plain"; attachment["data"] = base64; IDictionary<string, object> attachmentDict = new Dictionary<string, object>(); attachmentDict[testAttachmentName] = attachment; var properties = new Dictionary<string, object>(); properties["foo"] = 1; properties["bar"] = false; properties["_attachments"] = attachmentDict; var rev1 = database.PutRevision(new RevisionInternal(properties), null, false); // Examine the attachment store: Assert.AreEqual(1, attachments.Count()); // Get the revision: var gotRev1 = database.GetDocument(rev1.GetDocId(), rev1.GetRevId(), true); var gotAttachmentDict = gotRev1.GetPropertyForKey("_attachments").AsDictionary<string, object>(); gotAttachmentDict[testAttachmentName] = gotAttachmentDict[testAttachmentName].AsDictionary<string, object>(); var innerDict = new Dictionary<string, object>(); innerDict["content_type"] = "text/plain"; innerDict["digest"] = "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE="; innerDict["length"] = 27; innerDict["stub"] = true; innerDict["revpos"] = 1; var expectAttachmentDict = new Dictionary<string, object>(); expectAttachmentDict[testAttachmentName] = innerDict; Assert.AreEqual(expectAttachmentDict, gotAttachmentDict); // Update the attachment directly: var attachv2 = Encoding.UTF8.GetBytes("Replaced body of attach"); var writer = new BlobStoreWriter(database.Attachments); writer.AppendData(attachv2); writer.Finish(); var gotExpectedErrorCode = false; try { database.UpdateAttachment(testAttachmentName, writer, "application/foo", AttachmentEncoding.None, rev1.GetDocId(), null); } catch (CouchbaseLiteException e) { gotExpectedErrorCode = (e.CBLStatus.Code == StatusCode.Conflict); } Assert.IsTrue(gotExpectedErrorCode); gotExpectedErrorCode = false; try { database.UpdateAttachment(testAttachmentName, new BlobStoreWriter(database.Attachments), "application/foo", AttachmentEncoding.None, rev1.GetDocId(), "1-fafafa"); } catch (CouchbaseLiteException e) { gotExpectedErrorCode = (e.CBLStatus.Code == StatusCode.Conflict); } Assert.IsTrue(gotExpectedErrorCode); gotExpectedErrorCode = false; RevisionInternal rev2 = null; try { rev2 = database.UpdateAttachment(testAttachmentName, writer, "application/foo", AttachmentEncoding.None, rev1.GetDocId(), rev1.GetRevId()); } catch (CouchbaseLiteException) { gotExpectedErrorCode = true; } Assert.IsFalse(gotExpectedErrorCode); Assert.AreEqual(rev1.GetDocId(), rev2.GetDocId()); Assert.AreEqual(2, rev2.GetGeneration()); // Get the updated revision: RevisionInternal gotRev2 = database.GetDocument(rev2.GetDocId(), rev2 .GetRevId(), true); attachmentDict = gotRev2.GetProperties().Get("_attachments").AsDictionary<string, object>(); attachmentDict[testAttachmentName] = attachmentDict[testAttachmentName].AsDictionary<string, object>(); innerDict = new Dictionary<string, object>(); innerDict["content_type"] = "application/foo"; innerDict["digest"] = "sha1-mbT3208HI3PZgbG4zYWbDW2HsPk="; innerDict["length"] = 23; innerDict["stub"] = true; innerDict["revpos"] = 2; expectAttachmentDict[testAttachmentName] = innerDict; Assert.AreEqual(expectAttachmentDict, attachmentDict); // Delete the attachment: gotExpectedErrorCode = false; try { database.UpdateAttachment("nosuchattach", null, "application/foo", AttachmentEncoding.None, rev2.GetDocId(), rev2.GetRevId()); } catch (CouchbaseLiteException e) { gotExpectedErrorCode = (e.CBLStatus.Code == StatusCode.AttachmentNotFound); } Assert.IsTrue(gotExpectedErrorCode); gotExpectedErrorCode = false; try { database.UpdateAttachment("nosuchattach", null, null, AttachmentEncoding.None, "nosuchdoc", "nosuchrev"); } catch (CouchbaseLiteException e) { gotExpectedErrorCode = (e.CBLStatus.Code == StatusCode.NotFound); } Assert.IsTrue(gotExpectedErrorCode); RevisionInternal rev3 = database.UpdateAttachment(testAttachmentName, null, null, AttachmentEncoding.None, rev2.GetDocId(), rev2.GetRevId()); Assert.AreEqual(rev2.GetDocId(), rev3.GetDocId()); Assert.AreEqual(3, rev3.GetGeneration()); // Get the updated revision: RevisionInternal gotRev3 = database.GetDocument(rev3.GetDocId(), rev3 .GetRevId(), true); attachmentDict = gotRev3.GetProperties().Get("_attachments").AsDictionary<string, object>(); Assert.IsNull(attachmentDict); database.Close(); }
/// <summary>Updates or deletes an attachment, creating a new document revision in the process. /// </summary> /// <remarks> /// Updates or deletes an attachment, creating a new document revision in the process. /// Used by the PUT / DELETE methods called on attachment URLs. Used by the listener; /// </remarks> internal RevisionInternal UpdateAttachment(string filename, BlobStoreWriter body, string contentType, AttachmentEncoding encoding, string docID, RevisionID oldRevID, Uri source) { if(StringEx.IsNullOrWhiteSpace(filename)) { throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadAttachment, TAG, "Invalid filename (null or whitespace) in UpdateAttachment"); } if(body != null && contentType == null) { throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadAttachment, TAG, "Body provided, but content type is null in UpdateAttachment"); } if(oldRevID != null && docID == null) { throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadAttachment, TAG, "oldRevID provided ({0}) but docID is null in UpdateAttachment", oldRevID); } if(body != null && docID == null) { throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadAttachment, TAG, "body provided but docID is null in UpdateAttachment"); } var oldRev = new RevisionInternal(docID, oldRevID, false); if(oldRevID != null) { // Load existing revision if this is a replacement: try { oldRev = LoadRevisionBody(oldRev); } catch(CouchbaseLiteException e) { if(e.Code == StatusCode.NotFound && GetDocument(docID, null, false) != null) { throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.Conflict, TAG, "Conflict detected in UpdateAttachment"); } Log.To.Database.E(TAG, "Error loading revision body in UpdateAttachment, rethrowing..."); throw; } } else { // If this creates a new doc, it needs a body: oldRev.SetBody(new Body(new Dictionary<string, object>())); } // Update the _attachments dictionary: var attachments = oldRev.GetProperties().Get("_attachments").AsDictionary<string, object>(); if(attachments == null) { attachments = new Dictionary<string, object>(); } if(body != null) { var key = body.GetBlobKey(); string digest = key.Base64Digest(); RememberAttachmentWriter(body, digest); string encodingName = (encoding == AttachmentEncoding.GZIP) ? "gzip" : null; attachments[filename] = new NonNullDictionary<string, object> { { "digest", digest }, { "length", body.GetLength() }, { "follows", true }, { "content_type", contentType }, { "encoding", encodingName } }; } else { if(oldRevID != null && attachments.Get(filename) == null) { throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.AttachmentNotFound, TAG, "Attachment {0} not found", new SecureLogString(filename, LogMessageSensitivity.PotentiallyInsecure)); } attachments.Remove(filename); } var properties = oldRev.GetProperties(); properties["_attachments"] = attachments; oldRev.SetProperties(properties); var newRev = PutRevision(oldRev, oldRevID, false, source); return newRev; }
/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> public virtual void TestPutAttachment() { string testAttachmentName = "test_attachment"; BlobStore attachments = database.GetAttachments(); attachments.DeleteBlobs(); NUnit.Framework.Assert.AreEqual(0, attachments.Count()); // Put a revision that includes an _attachments dict: byte[] attach1 = Sharpen.Runtime.GetBytesForString("This is the body of attach1"); string base64 = Base64.EncodeBytes(attach1); IDictionary<string, object> attachment = new Dictionary<string, object>(); attachment.Put("content_type", "text/plain"); attachment.Put("data", base64); IDictionary<string, object> attachmentDict = new Dictionary<string, object>(); attachmentDict.Put(testAttachmentName, attachment); IDictionary<string, object> properties = new Dictionary<string, object>(); properties.Put("foo", 1); properties.Put("bar", false); properties.Put("_attachments", attachmentDict); RevisionInternal rev1 = database.PutRevision(new RevisionInternal(properties, database ), null, false); // Examine the attachment store: NUnit.Framework.Assert.AreEqual(1, attachments.Count()); // Get the revision: RevisionInternal gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1 .GetRevId(), EnumSet.NoneOf<Database.TDContentOptions>()); IDictionary<string, object> gotAttachmentDict = (IDictionary<string, object>)gotRev1 .GetProperties().Get("_attachments"); IDictionary<string, object> innerDict = new Dictionary<string, object>(); innerDict.Put("content_type", "text/plain"); innerDict.Put("digest", "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE="); innerDict.Put("length", 27); innerDict.Put("stub", true); innerDict.Put("revpos", 1); IDictionary<string, object> expectAttachmentDict = new Dictionary<string, object> (); expectAttachmentDict.Put(testAttachmentName, innerDict); NUnit.Framework.Assert.AreEqual(expectAttachmentDict, gotAttachmentDict); // Update the attachment directly: byte[] attachv2 = Sharpen.Runtime.GetBytesForString("Replaced body of attach"); bool gotExpectedErrorCode = false; BlobStoreWriter blobWriter = new BlobStoreWriter(database.GetAttachments()); blobWriter.AppendData(attachv2); blobWriter.Finish(); try { database.UpdateAttachment(testAttachmentName, blobWriter, "application/foo", AttachmentInternal.AttachmentEncoding .AttachmentEncodingNone, rev1.GetDocId(), null); } catch (CouchbaseLiteException e) { gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == Status.Conflict); } NUnit.Framework.Assert.IsTrue(gotExpectedErrorCode); gotExpectedErrorCode = false; try { database.UpdateAttachment(testAttachmentName, blobWriter, "application/foo", AttachmentInternal.AttachmentEncoding .AttachmentEncodingNone, rev1.GetDocId(), "1-bogus"); } catch (CouchbaseLiteException e) { gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == Status.Conflict); } NUnit.Framework.Assert.IsTrue(gotExpectedErrorCode); gotExpectedErrorCode = false; RevisionInternal rev2 = null; try { rev2 = database.UpdateAttachment(testAttachmentName, blobWriter, "application/foo" , AttachmentInternal.AttachmentEncoding.AttachmentEncodingNone, rev1.GetDocId(), rev1.GetRevId()); } catch (CouchbaseLiteException) { gotExpectedErrorCode = true; } NUnit.Framework.Assert.IsFalse(gotExpectedErrorCode); NUnit.Framework.Assert.AreEqual(rev1.GetDocId(), rev2.GetDocId()); NUnit.Framework.Assert.AreEqual(2, rev2.GetGeneration()); // Get the updated revision: RevisionInternal gotRev2 = database.GetDocumentWithIDAndRev(rev2.GetDocId(), rev2 .GetRevId(), EnumSet.NoneOf<Database.TDContentOptions>()); attachmentDict = (IDictionary<string, object>)gotRev2.GetProperties().Get("_attachments" ); innerDict = new Dictionary<string, object>(); innerDict.Put("content_type", "application/foo"); innerDict.Put("digest", "sha1-mbT3208HI3PZgbG4zYWbDW2HsPk="); innerDict.Put("length", 23); innerDict.Put("stub", true); innerDict.Put("revpos", 2); expectAttachmentDict.Put(testAttachmentName, innerDict); NUnit.Framework.Assert.AreEqual(expectAttachmentDict, attachmentDict); // Delete the attachment: gotExpectedErrorCode = false; try { database.UpdateAttachment("nosuchattach", null, null, AttachmentInternal.AttachmentEncoding .AttachmentEncodingNone, rev2.GetDocId(), rev2.GetRevId()); } catch (CouchbaseLiteException e) { gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == Status.NotFound); } NUnit.Framework.Assert.IsTrue(gotExpectedErrorCode); gotExpectedErrorCode = false; try { database.UpdateAttachment("nosuchattach", null, null, AttachmentInternal.AttachmentEncoding .AttachmentEncodingNone, "nosuchdoc", "nosuchrev"); } catch (CouchbaseLiteException e) { gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == Status.NotFound); } NUnit.Framework.Assert.IsTrue(gotExpectedErrorCode); RevisionInternal rev3 = database.UpdateAttachment(testAttachmentName, null, null, AttachmentInternal.AttachmentEncoding.AttachmentEncodingNone, rev2.GetDocId(), rev2 .GetRevId()); NUnit.Framework.Assert.AreEqual(rev2.GetDocId(), rev3.GetDocId()); NUnit.Framework.Assert.AreEqual(3, rev3.GetGeneration()); // Get the updated revision: RevisionInternal gotRev3 = database.GetDocumentWithIDAndRev(rev3.GetDocId(), rev3 .GetRevId(), EnumSet.NoneOf<Database.TDContentOptions>()); attachmentDict = (IDictionary<string, object>)gotRev3.GetProperties().Get("_attachments" ); NUnit.Framework.Assert.IsNull(attachmentDict); database.Close(); }
public void StartedPart(IDictionary<String, String> headers) { if (document == null) { jsonBuffer = new List<Byte>(1024); } else { curAttachment = database.GetAttachmentWriter(); var contentDisposition = headers.Get("Content-Disposition"); if (contentDisposition != null && contentDisposition.StartsWith("attachment; filename=")) { // TODO: Parse this less simplistically. Right now it assumes it's in exactly the same // format generated by -[CBL_Pusher uploadMultipartRevision:]. CouchDB (as of 1.2) doesn't // output any headers at all on attachments so there's no compatibility issue yet. var contentDispositionUnquoted = Misc.UnquoteString(contentDisposition); var name = contentDispositionUnquoted.Substring(21); if (name != null) { attachmentsByName.Put(name, curAttachment); } } } }
/// <summary>Updates or deletes an attachment, creating a new document revision in the process. /// </summary> /// <remarks> /// Updates or deletes an attachment, creating a new document revision in the process. /// Used by the PUT / DELETE methods called on attachment URLs. /// </remarks> /// <exclude></exclude> /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> internal RevisionInternal UpdateAttachment(string filename, BlobStoreWriter body, string contentType, AttachmentEncoding encoding, string docID, string oldRevID) { if(StringEx.IsNullOrWhiteSpace(filename) || (body != null && contentType == null) || (oldRevID != null && docID == null) || (body != null && docID == null)) { throw new CouchbaseLiteException(StatusCode.BadAttachment); } var oldRev = new RevisionInternal(docID, oldRevID, false); if (oldRevID != null) { // Load existing revision if this is a replacement: try { oldRev = LoadRevisionBody(oldRev); } catch (CouchbaseLiteException e) { if (e.Code == StatusCode.NotFound && GetDocument(docID, null, false) != null) { throw new CouchbaseLiteException(StatusCode.Conflict); } throw; } } else { // If this creates a new doc, it needs a body: oldRev.SetBody(new Body(new Dictionary<string, object>())); } // Update the _attachments dictionary: var attachments = oldRev.GetProperties().Get("_attachments").AsDictionary<string, object>(); if (attachments == null) { attachments = new Dictionary<string, object>(); } if (body != null) { var key = body.GetBlobKey(); string digest = key.Base64Digest(); RememberAttachmentWriter(body); string encodingName = (encoding == AttachmentEncoding.GZIP) ? "gzip" : null; attachments[filename] = new NonNullDictionary<string, object> { { "digest", digest }, { "length", body.GetLength() }, { "follows", true }, { "content_type", contentType }, { "encoding", encodingName } }; } else { if (oldRevID != null && attachments.Get(filename) == null) { throw new CouchbaseLiteException(StatusCode.AttachmentNotFound); } attachments.Remove(filename); } var properties = oldRev.GetProperties(); properties["_attachments"] = attachments; oldRev.SetProperties(properties); Status status = new Status(); var newRev = PutRevision(oldRev, oldRevID, false, status); if (status.IsError) { throw new CouchbaseLiteException(status.Code); } return newRev; }
public AtomicAction ActionToChangeEncryptionKey(SymmetricKey newKey) { var action = new AtomicAction(); // Find all the blob files: var blobs = default(string[]); var oldKey = EncryptionKey; blobs = Directory.GetFiles(_path, "*" + FileExtension); if (blobs.Length == 0) { // No blobs, so nothing to encrypt. Just add/remove the encryption marker file: action.AddLogic(() => { Log.D(TAG, "{0} {1}", (newKey != null) ? "encrypting" : "decrypting", _path); Log.D(TAG, " No blobs to copy; done."); EncryptionKey = newKey; MarkEncrypted(newKey != null); }, () => { EncryptionKey = oldKey; MarkEncrypted(oldKey != null); }, null); return(action); } // Create a new directory for the new blob store. Have to do this now, before starting the // action, because farther down we create an action to move it... var tempPath = Path.Combine(Path.GetTempPath(), String.Format("CouchbaseLite-Temp-{0}", Misc.CreateGUID())); action.AddLogic(() => { Log.D(TAG, "{0} {1}", (newKey != null) ? "encrypting" : "decrypting", _path); Directory.CreateDirectory(tempPath); }, () => Directory.Delete(tempPath, true), null); var tempStore = default(BlobStore); action.AddLogic(() => { tempStore = new BlobStore(tempPath, newKey); tempStore.MarkEncrypted(true); }, null, null); // Copy each of my blobs into the new store (which will update its encryption): action.AddLogic(() => { foreach (var blobName in blobs) { // Copy file by reading with old key and writing with new one: Log.D(TAG, " Copying {0}", blobName); Stream readStream = File.Open(blobName, FileMode.Open, FileAccess.Read, FileShare.Read); if (EncryptionKey != null) { readStream = EncryptionKey.DecryptStream(readStream); } var writer = new BlobStoreWriter(tempStore); try { writer.Read(readStream); writer.Finish(); writer.Install(); } catch (Exception) { writer.Cancel(); throw; } finally { readStream.Dispose(); } } }, null, null); // Replace the attachment dir with the new one: action.AddLogic(AtomicAction.MoveDirectory(tempPath, _path)); // Finally update EncryptionKey: action.AddLogic(() => { EncryptionKey = newKey; }, () => { EncryptionKey = oldKey; }, null); return(action); }
internal void RememberAttachmentWriter(BlobStoreWriter writer) { GetPendingAttachmentsByDigest().Put(writer.MD5DigestString(), writer); }
public void TestStreamAttachmentBlobStoreWriter() { var attachments = database.Attachments; var blobWriter = new BlobStoreWriter(attachments); var testBlob = "foo"; blobWriter.AppendData(Encoding.UTF8.GetBytes(testBlob)); blobWriter.Finish(); var sha1Base64Digest = "sha1-C+7Hteo/D9vJXQ3UfzxbwnXaijM="; Assert.AreEqual(blobWriter.SHA1DigestString(), sha1Base64Digest); // install it blobWriter.Install(); // look it up in blob store and make sure it's there var blobKey = new BlobKey(sha1Base64Digest); var blob = attachments.BlobForKey(blobKey); Assert.IsTrue(Arrays.Equals(Encoding.UTF8.GetBytes(testBlob).ToArray(), blob)); }
public virtual void TestPutAttachment() { const string testAttachmentName = "test_attachment"; var attachments = database.Attachments; attachments.DeleteBlobs(); Assert.AreEqual(0, attachments.Count()); // Put a revision that includes an _attachments dict: var attach1 = Encoding.UTF8.GetBytes("This is the body of attach1"); var base64 = Convert.ToBase64String(attach1); var attachment = new Dictionary <string, object>(); attachment["content_type"] = "text/plain"; attachment["data"] = base64; IDictionary <string, object> attachmentDict = new Dictionary <string, object>(); attachmentDict[testAttachmentName] = attachment; var properties = new Dictionary <string, object>(); properties["foo"] = 1; properties["bar"] = false; properties["_attachments"] = attachmentDict; var rev1 = database.PutRevision(new RevisionInternal(properties), null, false); // Examine the attachment store: Assert.AreEqual(1, attachments.Count()); // Get the revision: var gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1.GetRevId(), DocumentContentOptions.None); var gotAttachmentDict = gotRev1.GetPropertyForKey("_attachments").AsDictionary <string, object>(); var innerDict = new JObject(); innerDict["content_type"] = "text/plain"; innerDict["digest"] = "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE="; innerDict["length"] = 27; innerDict["stub"] = true; innerDict["revpos"] = 1; var expectAttachmentDict = new Dictionary <string, object>(); expectAttachmentDict[testAttachmentName] = innerDict; Assert.AreEqual(expectAttachmentDict, gotAttachmentDict); // Update the attachment directly: var attachv2 = Encoding.UTF8.GetBytes("Replaced body of attach"); var writer = new BlobStoreWriter(database.Attachments); writer.AppendData(attachv2); writer.Finish(); var gotExpectedErrorCode = false; try { database.UpdateAttachment(testAttachmentName, writer, "application/foo", AttachmentEncoding.None, rev1.GetDocId(), null); } catch (CouchbaseLiteException e) { gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == StatusCode.Conflict); } Assert.IsTrue(gotExpectedErrorCode); gotExpectedErrorCode = false; try { database.UpdateAttachment(testAttachmentName, new BlobStoreWriter(database.Attachments), "application/foo", AttachmentEncoding.None, rev1.GetDocId(), "1-bogus"); } catch (CouchbaseLiteException e) { gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == StatusCode.Conflict); } Assert.IsTrue(gotExpectedErrorCode); gotExpectedErrorCode = false; RevisionInternal rev2 = null; try { rev2 = database.UpdateAttachment(testAttachmentName, writer, "application/foo", AttachmentEncoding.None, rev1.GetDocId(), rev1.GetRevId()); } catch (CouchbaseLiteException) { gotExpectedErrorCode = true; } Assert.IsFalse(gotExpectedErrorCode); Assert.AreEqual(rev1.GetDocId(), rev2.GetDocId()); Assert.AreEqual(2, rev2.GetGeneration()); // Get the updated revision: RevisionInternal gotRev2 = database.GetDocumentWithIDAndRev(rev2.GetDocId(), rev2 .GetRevId(), DocumentContentOptions.None); attachmentDict = gotRev2.GetProperties().Get("_attachments").AsDictionary <string, object>(); innerDict = new JObject(); innerDict["content_type"] = "application/foo"; innerDict["digest"] = "sha1-mbT3208HI3PZgbG4zYWbDW2HsPk="; innerDict["length"] = 23; innerDict["stub"] = true; innerDict["revpos"] = 2; expectAttachmentDict[testAttachmentName] = innerDict; Assert.AreEqual(expectAttachmentDict, attachmentDict); // Delete the attachment: gotExpectedErrorCode = false; try { database.UpdateAttachment("nosuchattach", null, "application/foo", AttachmentEncoding.None, rev2.GetDocId(), rev2.GetRevId()); } catch (CouchbaseLiteException e) { gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == StatusCode.NotFound); } Assert.IsTrue(gotExpectedErrorCode); gotExpectedErrorCode = false; try { database.UpdateAttachment("nosuchattach", null, null, AttachmentEncoding.None, "nosuchdoc", "nosuchrev"); } catch (CouchbaseLiteException e) { gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == StatusCode.NotFound); } Assert.IsTrue(gotExpectedErrorCode); RevisionInternal rev3 = database.UpdateAttachment(testAttachmentName, null, null, AttachmentEncoding.None, rev2.GetDocId(), rev2.GetRevId()); Assert.AreEqual(rev2.GetDocId(), rev3.GetDocId()); Assert.AreEqual(3, rev3.GetGeneration()); // Get the updated revision: RevisionInternal gotRev3 = database.GetDocumentWithIDAndRev(rev3.GetDocId(), rev3 .GetRevId(), DocumentContentOptions.None); attachmentDict = gotRev3.GetProperties().Get("_attachments").AsDictionary <string, object>(); Assert.IsNull(attachmentDict); database.Close(); }
public void FinishedPart() { if (jsonBuffer != null) { ParseJsonBuffer(); } else { curAttachment.Finish(); String md5String = curAttachment.MD5DigestString(); attachmentsByMd5Digest.Put(md5String, curAttachment); curAttachment = null; } }
/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> public virtual void TestPutAttachment() { string testAttachmentName = "test_attachment"; BlobStore attachments = database.GetAttachments(); attachments.DeleteBlobs(); NUnit.Framework.Assert.AreEqual(0, attachments.Count()); // Put a revision that includes an _attachments dict: byte[] attach1 = Sharpen.Runtime.GetBytesForString("This is the body of attach1"); string base64 = Base64.EncodeBytes(attach1); IDictionary <string, object> attachment = new Dictionary <string, object>(); attachment.Put("content_type", "text/plain"); attachment.Put("data", base64); IDictionary <string, object> attachmentDict = new Dictionary <string, object>(); attachmentDict.Put(testAttachmentName, attachment); IDictionary <string, object> properties = new Dictionary <string, object>(); properties.Put("foo", 1); properties.Put("bar", false); properties.Put("_attachments", attachmentDict); RevisionInternal rev1 = database.PutRevision(new RevisionInternal(properties, database ), null, false); // Examine the attachment store: NUnit.Framework.Assert.AreEqual(1, attachments.Count()); // Get the revision: RevisionInternal gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1 .GetRevId(), EnumSet.NoneOf <Database.TDContentOptions>()); IDictionary <string, object> gotAttachmentDict = (IDictionary <string, object>)gotRev1 .GetProperties().Get("_attachments"); IDictionary <string, object> innerDict = new Dictionary <string, object>(); innerDict.Put("content_type", "text/plain"); innerDict.Put("digest", "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE="); innerDict.Put("length", 27); innerDict.Put("stub", true); innerDict.Put("revpos", 1); IDictionary <string, object> expectAttachmentDict = new Dictionary <string, object> (); expectAttachmentDict.Put(testAttachmentName, innerDict); NUnit.Framework.Assert.AreEqual(expectAttachmentDict, gotAttachmentDict); // Update the attachment directly: byte[] attachv2 = Sharpen.Runtime.GetBytesForString("Replaced body of attach"); bool gotExpectedErrorCode = false; BlobStoreWriter blobWriter = new BlobStoreWriter(database.GetAttachments()); blobWriter.AppendData(attachv2); blobWriter.Finish(); try { database.UpdateAttachment(testAttachmentName, blobWriter, "application/foo", AttachmentInternal.AttachmentEncoding .AttachmentEncodingNone, rev1.GetDocId(), null); } catch (CouchbaseLiteException e) { gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == Status.Conflict); } NUnit.Framework.Assert.IsTrue(gotExpectedErrorCode); gotExpectedErrorCode = false; try { database.UpdateAttachment(testAttachmentName, blobWriter, "application/foo", AttachmentInternal.AttachmentEncoding .AttachmentEncodingNone, rev1.GetDocId(), "1-bogus"); } catch (CouchbaseLiteException e) { gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == Status.Conflict); } NUnit.Framework.Assert.IsTrue(gotExpectedErrorCode); gotExpectedErrorCode = false; RevisionInternal rev2 = null; try { rev2 = database.UpdateAttachment(testAttachmentName, blobWriter, "application/foo" , AttachmentInternal.AttachmentEncoding.AttachmentEncodingNone, rev1.GetDocId(), rev1.GetRevId()); } catch (CouchbaseLiteException) { gotExpectedErrorCode = true; } NUnit.Framework.Assert.IsFalse(gotExpectedErrorCode); NUnit.Framework.Assert.AreEqual(rev1.GetDocId(), rev2.GetDocId()); NUnit.Framework.Assert.AreEqual(2, rev2.GetGeneration()); // Get the updated revision: RevisionInternal gotRev2 = database.GetDocumentWithIDAndRev(rev2.GetDocId(), rev2 .GetRevId(), EnumSet.NoneOf <Database.TDContentOptions>()); attachmentDict = (IDictionary <string, object>)gotRev2.GetProperties().Get("_attachments" ); innerDict = new Dictionary <string, object>(); innerDict.Put("content_type", "application/foo"); innerDict.Put("digest", "sha1-mbT3208HI3PZgbG4zYWbDW2HsPk="); innerDict.Put("length", 23); innerDict.Put("stub", true); innerDict.Put("revpos", 2); expectAttachmentDict.Put(testAttachmentName, innerDict); NUnit.Framework.Assert.AreEqual(expectAttachmentDict, attachmentDict); // Delete the attachment: gotExpectedErrorCode = false; try { database.UpdateAttachment("nosuchattach", null, null, AttachmentInternal.AttachmentEncoding .AttachmentEncodingNone, rev2.GetDocId(), rev2.GetRevId()); } catch (CouchbaseLiteException e) { gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == Status.NotFound); } NUnit.Framework.Assert.IsTrue(gotExpectedErrorCode); gotExpectedErrorCode = false; try { database.UpdateAttachment("nosuchattach", null, null, AttachmentInternal.AttachmentEncoding .AttachmentEncodingNone, "nosuchdoc", "nosuchrev"); } catch (CouchbaseLiteException e) { gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == Status.NotFound); } NUnit.Framework.Assert.IsTrue(gotExpectedErrorCode); RevisionInternal rev3 = database.UpdateAttachment(testAttachmentName, null, null, AttachmentInternal.AttachmentEncoding.AttachmentEncodingNone, rev2.GetDocId(), rev2 .GetRevId()); NUnit.Framework.Assert.AreEqual(rev2.GetDocId(), rev3.GetDocId()); NUnit.Framework.Assert.AreEqual(3, rev3.GetGeneration()); // Get the updated revision: RevisionInternal gotRev3 = database.GetDocumentWithIDAndRev(rev3.GetDocId(), rev3 .GetRevId(), EnumSet.NoneOf <Database.TDContentOptions>()); attachmentDict = (IDictionary <string, object>)gotRev3.GetProperties().Get("_attachments" ); NUnit.Framework.Assert.IsNull(attachmentDict); database.Close(); }