/// <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="
                                                                 ));
        }
Exemple #2
0
        public void TestReopen()
        {
            var item = Encoding.UTF8.GetBytes("this is an item");
            var key  = new BlobKey();

            _store.StoreBlob(item, key);

            var store2   = new BlobStore(_storePath, _store.EncryptionKey);
            var readItem = store2.BlobForKey(key);

            readItem.Should().Equal(item, "because the contents of a key should be the same in the second store");

            readItem = _store.BlobForKey(key);
            readItem.Should().Equal(item, "because the contents of a key should be the same in the first store");
            Verify(key, item);
        }
Exemple #3
0
        /// <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
                                          );
        }
Exemple #4
0
        public void Setup()
        {
            _storePath = Path.GetTempPath();
            _storePath = Path.Combine(_storePath, "CBL_BlobStore");
            if (Directory.Exists(_storePath))
            {
                Directory.Delete(_storePath, true);
            }

            if (_encrypt)
            {
                Trace.WriteLine("----- Now enabling attachment encryption ----");
            }

            _store = new BlobStore(_storePath, _encrypt ? new SymmetricKey() : null);
            var encryptionMarkerPath = Path.Combine(_storePath, BlobStore.EncryptionMarkerFilename);
            var markerExists         = File.Exists(encryptionMarkerPath);

            markerExists.Should().Be(_encrypt);
        }
Exemple #5
0
        public BlobStoreWriter(BlobStore store)
        {
            this.store = store;
            try
            {
                sha1Digest = MessageDigest.GetInstance("SHA-1");
                sha1Digest.Reset();
                md5Digest = MessageDigest.GetInstance("MD5");
                md5Digest.Reset();
            } catch (NotSupportedException e) {
                throw Misc.CreateExceptionAndLog(Log.To.Database, e, Tag,
                                                 "Could not get an instance of SHA-1 or MD5 for BlobStoreWriter.");
            }

            try {
                OpenTempFile();
            } catch (FileNotFoundException e) {
                throw Misc.CreateExceptionAndLog(Log.To.Database, e, Tag,
                                                 "Unable to open temporary file for BlobStoreWriter.");
            }
        }
Exemple #6
0
        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 BlobStoreWriter(BlobStore store)
 {
     this.store = store;
     try
     {
         sha1Digest = MessageDigest.GetInstance("SHA-1");
         sha1Digest.Reset();
         md5Digest = MessageDigest.GetInstance("MD5");
         md5Digest.Reset();
     }
     catch (NoSuchAlgorithmException e)
     {
         throw new InvalidOperationException("Could not get an instance of SHA-1 or MD5.", e);
     }
     try
     {
         OpenTempFile();
     }
     catch (FileNotFoundException e)
     {
         throw new InvalidOperationException("Unable to open temporary file.", e);
     }
 }
		public BlobStoreWriter(BlobStore store)
		{
			this.store = store;
			try
			{
				sha1Digest = MessageDigest.GetInstance("SHA-1");
				sha1Digest.Reset();
				md5Digest = MessageDigest.GetInstance("MD5");
				md5Digest.Reset();
			}
			catch (NoSuchAlgorithmException e)
			{
				throw new InvalidOperationException(e);
			}
			try
			{
				OpenTempFile();
			}
			catch (FileNotFoundException e)
			{
				throw new InvalidOperationException(e);
			}
		}
 public BlobStoreWriter(BlobStore store)
 {
     this.store = store;
     try
     {
         sha1Digest = MessageDigest.GetInstance("SHA-1");
         sha1Digest.Reset();
         md5Digest = MessageDigest.GetInstance("MD5");
         md5Digest.Reset();
     }
     catch (NoSuchAlgorithmException e)
     {
         throw new InvalidOperationException(e);
     }
     try
     {
         OpenTempFile();
     }
     catch (FileNotFoundException e)
     {
         throw new InvalidOperationException(e);
     }
 }
		public BlobStoreWriter(BlobStore store)
		{
			this.store = store;
			try
			{
				sha1Digest = MessageDigest.GetInstance("SHA-1");
				sha1Digest.Reset();
				md5Digest = MessageDigest.GetInstance("MD5");
				md5Digest.Reset();
			}
			catch (NoSuchAlgorithmException e)
			{
                throw new InvalidOperationException("Could not get an instance of SHA-1 or MD5." , e);
			}
			try
			{
				OpenTempFile();
			}
			catch (FileNotFoundException e)
			{
                throw new InvalidOperationException("Unable to open temporary file.", e);
			}
		}
        internal bool Open()
        {
            if (_isOpen) {
                return true;
            }

            Log.D(TAG, "Opening {0}", Name);

            // Instantiate storage:
            //string storageType = Manager.StorageType ?? "SQLite";
            Storage = new SqliteCouchStore();
            Storage.Delegate = this;

            Log.D(TAG, "Using {0} for db at {1}", Storage.GetType(), Path);
            if (!Storage.Open(Path, Manager)) {
                return false;
            }

            Storage.AutoCompact = AUTO_COMPACT;

            // First-time setup:
            if (PrivateUUID() == null) {
                Storage.SetInfo("privateUUID", Misc.CreateGUID());
                Storage.SetInfo("publicUUID", Misc.CreateGUID());
            }

            var savedMaxRevDepth = _maxRevTreeDepth != 0 ? _maxRevTreeDepth.ToString() : Storage.GetInfo("max_revs");
            int maxRevTreeDepth = 0;
            if (savedMaxRevDepth != null && int.TryParse(savedMaxRevDepth, out maxRevTreeDepth)) {
                MaxRevTreeDepth = maxRevTreeDepth;
            } else {
                MaxRevTreeDepth = DEFAULT_MAX_REVS;
            }

            // Open attachment store:
            string attachmentsPath = AttachmentStorePath;

            try {
                Attachments = new BlobStore(attachmentsPath);
            } catch(Exception e) {
                Log.W(TAG, String.Format("Couldn't open attachment store at {0}", attachmentsPath), e);
                Storage.Close();
                Storage = null;
                return false;
            }
           
            _isOpen = true;
            return true;
        }
Exemple #12
0
        public void TestAttachments()
        {
            const string testAttachmentName = "test_attachment";
            var          attachments        = database.Attachments;

            Assert.AreEqual(0, attachments.Count());
            Assert.AreEqual(0, attachments.AllKeys().Count());

            var attach1 = Encoding.UTF8.GetBytes("This is the body of attach1");
            var props   = new Dictionary <string, object> {
                { "foo", 1 },
                { "bar", false },
                { "_attachments", CreateAttachmentsDict(attach1, testAttachmentName, "text/plain", false) }
            };

            Status           status = new Status();
            RevisionInternal rev1   = database.PutRevision(new RevisionInternal(props), null, false, status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());

            var att = database.GetAttachmentForRevision(rev1, testAttachmentName, status);

            Assert.IsNotNull(att, "Couldn't get attachment:  Status {0}", status.GetCode());
            Assert.AreEqual(attach1, att.Content);
            Assert.AreEqual("text/plain", att.ContentType);
            Assert.AreEqual(AttachmentEncoding.None, att.Encoding);

            var itemDict = new Dictionary <string, object> {
                { "content_type", "text/plain" },
                { "digest", "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE=" },
                { "length", 27 },
                { "stub", true },
                { "revpos", 1 }
            };
            var attachmentDict = new Dictionary <string, object> {
                { testAttachmentName, itemDict }
            };
            var gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1.GetRevId(), DocumentContentOptions.None);

            AssertDictionariesAreEqual(attachmentDict, gotRev1.GetAttachments());

            itemDict.Remove("stub");
            itemDict["data"] = Convert.ToBase64String(attach1);
            gotRev1          = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1.GetRevId(), DocumentContentOptions.IncludeAttachments);
            var expandedRev = gotRev1.CopyWithDocID(rev1.GetDocId(), rev1.GetRevId());

            Assert.IsTrue(database.ExpandAttachments(expandedRev, 0, false, true, status));
            AssertDictionariesAreEqual(attachmentDict, expandedRev.GetAttachments());

            // Add a second revision that doesn't update the attachment:
            props = new Dictionary <string, object> {
                { "_id", rev1.GetDocId() },
                { "foo", 2 },
                { "bazz", false },
                { "_attachments", CreateAttachmentsStub(testAttachmentName) }
            };
            var rev2 = database.PutRevision(new RevisionInternal(props), rev1.GetRevId(), status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());

            // Add a third revision of the same document:
            var attach2 = Encoding.UTF8.GetBytes("<html>And this is attach2</html>");

            props = new Dictionary <string, object> {
                { "_id", rev2.GetDocId() },
                { "foo", 2 },
                { "bazz", false },
                { "_attachments", CreateAttachmentsDict(attach2, testAttachmentName, "text/html", false) }
            };
            var rev3 = database.PutRevision(new RevisionInternal(props), rev2.GetRevId(), status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());

            // Check the second revision's attachment
            att = database.GetAttachmentForRevision(rev2, testAttachmentName, status);
            Assert.IsNotNull(att, "Couldn't get attachment:  Status {0}", status.GetCode());
            Assert.AreEqual(attach1, att.Content);
            Assert.AreEqual("text/plain", att.ContentType);
            Assert.AreEqual(AttachmentEncoding.None, att.Encoding);

            expandedRev = rev2.CopyWithDocID(rev2.GetDocId(), rev2.GetRevId());
            Assert.IsTrue(database.ExpandAttachments(expandedRev, 2, false, true, status));
            AssertDictionariesAreEqual(new Dictionary <string, object> {
                { testAttachmentName, new Dictionary <string, object> {
                      { "stub", true },
                      { "revpos", 1 }
                  } }
            }, expandedRev.GetAttachments());

            // Check the 3rd revision's attachment:
            att = database.GetAttachmentForRevision(rev3, testAttachmentName, status);
            Assert.IsNotNull(att, "Couldn't get attachment:  Status {0}", status.GetCode());
            Assert.AreEqual(attach2, att.Content);
            Assert.AreEqual("text/html", att.ContentType);
            Assert.AreEqual(AttachmentEncoding.None, att.Encoding);

            expandedRev = rev3.CopyWithDocID(rev3.GetDocId(), rev3.GetRevId());
            Assert.IsTrue(database.ExpandAttachments(expandedRev, 2, false, true, status));
            attachmentDict = new Dictionary <string, object> {
                { testAttachmentName, new Dictionary <string, object> {
                      { "content_type", "text/html" },
                      { "data", "PGh0bWw+QW5kIHRoaXMgaXMgYXR0YWNoMjwvaHRtbD4=" },
                      { "digest", "sha1-s14XRTXlwvzYfjo1t1u0rjB+ZUA=" },
                      { "length", 32 },
                      { "revpos", 3 }
                  } }
            };
            AssertDictionariesAreEqual(attachmentDict, expandedRev.GetAttachments());

            // Examine the attachment store:
            Assert.AreEqual(2, attachments.Count());
            Assert.AreEqual(new HashSet <BlobKey> {
                BlobStore.KeyForBlob(attach1), BlobStore.KeyForBlob(attach2)
            }, attachments.AllKeys());
            database.Compact();
            Assert.AreEqual(1, attachments.Count());
            Assert.AreEqual(new HashSet <BlobKey> {
                BlobStore.KeyForBlob(attach2)
            }, attachments.AllKeys());
        }
Exemple #13
0
		public bool Open()
		{
			if (open)
			{
				return true;
			}
			// Create the storage engine.
			database = SQLiteStorageEngineFactory.CreateStorageEngine();
			// Try to open the storage engine and stop if we fail.
			if (database == null || !database.Open(path))
			{
				string msg = "Unable to create a storage engine, fatal error";
				Log.E(Database.Tag, msg);
				throw new InvalidOperationException(msg);
			}
			// Stuff we need to initialize every time the sqliteDb opens:
			if (!Initialize("PRAGMA foreign_keys = ON;"))
			{
				Log.E(Database.Tag, "Error turning on foreign keys");
				return false;
			}
			// Check the user_version number we last stored in the sqliteDb:
			int dbVersion = database.GetVersion();
			// Incompatible version changes increment the hundreds' place:
			if (dbVersion >= 100)
			{
				Log.W(Database.Tag, "Database: Database version (" + dbVersion + ") is newer than I know how to work with"
					);
				database.Close();
				return false;
			}
			if (dbVersion < 1)
			{
				// First-time initialization:
				// (Note: Declaring revs.sequence as AUTOINCREMENT means the values will always be
				// monotonically increasing, never reused. See <http://www.sqlite.org/autoinc.html>)
				if (!Initialize(Schema))
				{
					database.Close();
					return false;
				}
				dbVersion = 3;
			}
			if (dbVersion < 2)
			{
				// Version 2: added attachments.revpos
				string upgradeSql = "ALTER TABLE attachments ADD COLUMN revpos INTEGER DEFAULT 0; "
					 + "PRAGMA user_version = 2";
				if (!Initialize(upgradeSql))
				{
					database.Close();
					return false;
				}
				dbVersion = 2;
			}
			if (dbVersion < 3)
			{
				string upgradeSql = "CREATE TABLE localdocs ( " + "docid TEXT UNIQUE NOT NULL, " 
					+ "revid TEXT NOT NULL, " + "json BLOB); " + "CREATE INDEX localdocs_by_docid ON localdocs(docid); "
					 + "PRAGMA user_version = 3";
				if (!Initialize(upgradeSql))
				{
					database.Close();
					return false;
				}
				dbVersion = 3;
			}
			if (dbVersion < 4)
			{
				string upgradeSql = "CREATE TABLE info ( " + "key TEXT PRIMARY KEY, " + "value TEXT); "
					 + "INSERT INTO INFO (key, value) VALUES ('privateUUID', '" + Misc.TDCreateUUID(
					) + "'); " + "INSERT INTO INFO (key, value) VALUES ('publicUUID',  '" + Misc.TDCreateUUID
					() + "'); " + "PRAGMA user_version = 4";
				if (!Initialize(upgradeSql))
				{
					database.Close();
					return false;
				}
			}
			try
			{
				attachments = new BlobStore(GetAttachmentStorePath());
			}
			catch (ArgumentException e)
			{
				Log.E(Database.Tag, "Could not initialize attachment store", e);
				database.Close();
				return false;
			}
			open = true;
			return true;
		}
Exemple #14
0
		public RevisionInternal UpdateAttachment(string filename, InputStream contentStream
			, string contentType, string docID, string oldRevID)
		{
			bool isSuccessful = false;
			if (filename == null || filename.Length == 0 || (contentStream != null && contentType
				 == null) || (oldRevID != null && docID == null) || (contentStream != null && docID
				 == null))
			{
				throw new CouchbaseLiteException(Status.BadRequest);
			}
			BeginTransaction();
			try
			{
				RevisionInternal oldRev = new RevisionInternal(docID, oldRevID, false, this);
				if (oldRevID != null)
				{
					// Load existing revision if this is a replacement:
					try
					{
						LoadRevisionBody(oldRev, EnumSet.NoneOf<Database.TDContentOptions>());
					}
					catch (CouchbaseLiteException e)
					{
						if (e.GetCBLStatus().GetCode() == Status.NotFound && ExistsDocumentWithIDAndRev(docID
							, null))
						{
							throw new CouchbaseLiteException(Status.Conflict);
						}
					}
					IDictionary<string, object> oldRevProps = oldRev.GetProperties();
					IDictionary<string, object> attachments = null;
					if (oldRevProps != null)
					{
						attachments = (IDictionary<string, object>)oldRevProps.Get("_attachments");
					}
					if (contentStream == null && attachments != null && !attachments.ContainsKey(filename
						))
					{
						throw new CouchbaseLiteException(Status.NotFound);
					}
					// Remove the _attachments stubs so putRevision: doesn't copy the rows for me
					// OPT: Would be better if I could tell loadRevisionBody: not to add it
					if (attachments != null)
					{
						IDictionary<string, object> properties = new Dictionary<string, object>(oldRev.GetProperties
							());
						Sharpen.Collections.Remove(properties, "_attachments");
						oldRev.SetBody(new Body(properties));
					}
				}
				else
				{
					// If this creates a new doc, it needs a body:
					oldRev.SetBody(new Body(new Dictionary<string, object>()));
				}
				// Create a new revision:
				Status putStatus = new Status();
				RevisionInternal newRev = PutRevision(oldRev, oldRevID, false, putStatus);
				if (newRev == null)
				{
					return null;
				}
				if (oldRevID != null)
				{
					// Copy all attachment rows _except_ for the one being updated:
					string[] args = new string[] { System.Convert.ToString(newRev.GetSequence()), System.Convert.ToString
						(oldRev.GetSequence()), filename };
					database.ExecSQL("INSERT INTO attachments " + "(sequence, filename, key, type, length, revpos) "
						 + "SELECT ?, filename, key, type, length, revpos FROM attachments " + "WHERE sequence=? AND filename != ?"
						, args);
				}
				if (contentStream != null)
				{
					// If not deleting, add a new attachment entry:
					InsertAttachmentForSequenceWithNameAndType(contentStream, newRev.GetSequence(), filename
						, contentType, newRev.GetGeneration());
				}
				isSuccessful = true;
				return newRev;
			}
			catch (SQLException e)
			{
				Log.E(Tag, "Error updating attachment", e);
				throw new CouchbaseLiteException(new Status(Status.InternalServerError));
			}
			finally
			{
				EndTransaction(isSuccessful);
			}
		}
Exemple #15
0
		public void StubOutAttachmentsIn(RevisionInternal rev, int minRevPos)
		{
			if (minRevPos <= 1)
			{
				return;
			}
			IDictionary<string, object> properties = (IDictionary<string, object>)rev.GetProperties
				();
			IDictionary<string, object> attachments = null;
			if (properties != null)
			{
				attachments = (IDictionary<string, object>)properties.Get("_attachments");
			}
			IDictionary<string, object> editedProperties = null;
			IDictionary<string, object> editedAttachments = null;
			foreach (string name in attachments.Keys)
			{
				IDictionary<string, object> attachment = (IDictionary<string, object>)attachments
					.Get(name);
				int revPos = (int)attachment.Get("revpos");
				object stub = attachment.Get("stub");
				if (revPos > 0 && revPos < minRevPos && (stub == null))
				{
					// Strip this attachment's body. First make its dictionary mutable:
					if (editedProperties == null)
					{
						editedProperties = new Dictionary<string, object>(properties);
						editedAttachments = new Dictionary<string, object>(attachments);
						editedProperties.Put("_attachments", editedAttachments);
					}
					// ...then remove the 'data' and 'follows' key:
					IDictionary<string, object> editedAttachment = new Dictionary<string, object>(attachment
						);
					Sharpen.Collections.Remove(editedAttachment, "data");
					Sharpen.Collections.Remove(editedAttachment, "follows");
					editedAttachment.Put("stub", true);
					editedAttachments.Put(name, editedAttachment);
					Log.D(Database.Tag, "Stubbed out attachment" + rev + " " + name + ": revpos" + revPos
						 + " " + minRevPos);
				}
			}
			if (editedProperties != null)
			{
				rev.SetProperties(editedProperties);
			}
		}
        internal void OpenWithOptions(DatabaseOptions options)
        {
            if(IsOpen) {
                return;
            }

            Log.To.Database.I(TAG, "Opening {0}", this);
            _readonly = _readonly || options.ReadOnly;

            // Instantiate storage:
            string storageType = options.StorageType ?? Manager.StorageType ?? StorageEngineTypes.SQLite;
            var primaryStorage = GetStorageClass(storageType);

            if(primaryStorage == null) {
                if(storageType == StorageEngineTypes.SQLite) {
                    throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.InvalidStorageType, TAG,
                        "No implementation found for SQLite storage.  For more information, see " +
                        "https://github.com/couchbase/couchbase-lite-net/wiki/Error-Dictionary#cblcs0001");
                } else {
                    throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.InvalidStorageType, TAG,
                        "No implementation found for ForestDB storage.  For more information, see " +
                        "https://github.com/couchbase/couchbase-lite-net/wiki/Error-Dictionary#cblcs0002");
                }
            }


            var upgrade = false;
            var primarySQLite = storageType == StorageEngineTypes.SQLite;
            var otherStorage = primarySQLite ? GetStorageClass(StorageEngineTypes.ForestDB) :
                GetStorageClass(StorageEngineTypes.SQLite);


            var primaryStorageInstance = (ICouchStore)Activator.CreateInstance(primaryStorage);
            var otherStorageInstance = otherStorage != null ? (ICouchStore)Activator.CreateInstance(otherStorage) : null;
            if(options.StorageType != null) {
                // If explicit storage type given in options, always use primary storage type,
                // and if secondary db exists, try to upgrade from it:
                upgrade = otherStorageInstance != null && otherStorageInstance.DatabaseExistsIn(DbDirectory) &&
                    !primaryStorageInstance.DatabaseExistsIn(DbDirectory);

                if(upgrade && primarySQLite) {
                    throw Misc.CreateExceptionAndLog(Log.To.Upgrade, StatusCode.InvalidStorageType, TAG,
                        "Upgrades from ForestDB to SQLite are not supported.  For more information see " +
                        "https://github.com/couchbase/couchbase-lite-net/wiki/Error-Dictionary#cbldb0001");
                }
            } else {
                // If options don't specify, use primary unless secondary db already exists in dir:
                if(otherStorageInstance != null && otherStorageInstance.DatabaseExistsIn(DbDirectory)) {
                    primaryStorageInstance = otherStorageInstance;
                }
            }

            Log.To.Database.I(TAG, "Using {0} for db at {1}; upgrade={2}", primaryStorage.FullName, DbDirectory, upgrade);
            Storage = primaryStorageInstance;
            Storage.Delegate = this;
            Storage.AutoCompact = AUTO_COMPACT;

            // Encryption:
            var encryptionKey = options.EncryptionKey;
            if(encryptionKey != null) {
                Storage.SetEncryptionKey(encryptionKey);
            }

            // Open the storage!
            try {
                Storage.Open(DbDirectory, Manager, _readonly);
            } catch(CouchbaseLiteException) {
                Storage.Close();
                Log.To.Database.E(TAG, "Failed to open storage for database, rethrowing...");
                throw;
            } catch(Exception e) {
                Storage.Close();
                throw Misc.CreateExceptionAndLog(Log.To.Database, e, TAG, "Got exception while opening storage for database");
            }

            // First-time setup:
            if(PrivateUUID() == null) {
                Storage.SetInfo("privateUUID", Misc.CreateGUID());
                Storage.SetInfo("publicUUID", Misc.CreateGUID());
            }

            var savedMaxRevDepth = _maxRevTreeDepth != 0 ? _maxRevTreeDepth.ToString() : Storage.GetInfo("max_revs");
            int maxRevTreeDepth = 0;
            if(savedMaxRevDepth != null && int.TryParse(savedMaxRevDepth, out maxRevTreeDepth)) {
                SetMaxRevTreeDepth(maxRevTreeDepth);
            } else {
                SetMaxRevTreeDepth(Manager.DefaultMaxRevTreeDepth);
            }

            // Open attachment store:
            string attachmentsPath = AttachmentStorePath;

            try {
                Attachments = new BlobStore(attachmentsPath, encryptionKey);
            } catch(CouchbaseLiteException) {
                Log.To.Database.E(TAG, "Error creating blob store at {0}, rethrowing...", attachmentsPath);
                Storage.Close();
                Storage = null;
                throw;
            } catch(Exception e) {
                Storage.Close();
                Storage = null;
                throw Misc.CreateExceptionAndLog(Log.To.Database, e, TAG, "Got exception creating blob store at {0}", attachmentsPath);
            }

            IsOpen = true;

            if(upgrade) {
                var upgrader = primarySQLite ? Storage.CreateUpgrader(this, DbDirectory)
                    : otherStorageInstance.CreateUpgrader(this, DbDirectory);
                try {
                    upgrader.Import();
                } catch(CouchbaseLiteException e) {
                    Log.To.Database.E(TAG, "Upgrade failed for {0} (Status {1}), rethrowing...", DbDirectory, e.CBLStatus);
                    upgrader.Backout();
                    Close();
                    throw;
                }
            }

            _expirePurgeTimer = new Timer(PurgeExpired, null, HousekeepingDelayAfterOpen, TimeSpan.FromMilliseconds(-1));
        }
Exemple #17
0
        /// <exception cref="System.Exception"></exception>
        public virtual void TestPutLargeAttachment()
        {
            string    testAttachmentName = "test_attachment";
            BlobStore attachments        = database.GetAttachments();

            attachments.DeleteBlobs();
            NUnit.Framework.Assert.AreEqual(0, attachments.Count());
            Status status = new Status();
            IDictionary <string, object> rev1Properties = new Dictionary <string, object>();

            rev1Properties.Put("foo", 1);
            rev1Properties.Put("bar", false);
            RevisionInternal rev1 = database.PutRevision(new RevisionInternal(rev1Properties,
                                                                              database), null, false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            StringBuilder largeAttachment = new StringBuilder();

            for (int i = 0; i < Database.kBigAttachmentLength; i++)
            {
                largeAttachment.Append("big attachment!");
            }
            byte[] attach1 = Sharpen.Runtime.GetBytesForString(largeAttachment.ToString());
            database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach1
                                                                                         ), rev1.GetSequence(), testAttachmentName, "text/plain", rev1.GetGeneration());
            Attachment attachment = database.GetAttachmentForSequence(rev1.GetSequence(), testAttachmentName
                                                                      );

            NUnit.Framework.Assert.AreEqual("text/plain", attachment.GetContentType());
            byte[] data = IOUtils.ToByteArray(attachment.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
            EnumSet <Database.TDContentOptions> contentOptions = EnumSet.Of(Database.TDContentOptions
                                                                            .TDIncludeAttachments, Database.TDContentOptions.TDBigAttachmentsFollow);
            IDictionary <string, object> attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent
                                                                         (rev1.GetSequence(), contentOptions);
            IDictionary <string, object> innerDict = (IDictionary <string, object>)attachmentDictForSequence
                                                     .Get(testAttachmentName);

            if (!innerDict.ContainsKey("stub"))
            {
                throw new RuntimeException("Expected attachment dict to have 'stub' key");
            }
            if (((bool)innerDict.Get("stub")) == false)
            {
                throw new RuntimeException("Expected attachment dict 'stub' key to be true");
            }
            if (!innerDict.ContainsKey("follows"))
            {
                throw new RuntimeException("Expected attachment dict to have 'follows' key");
            }
            RevisionInternal rev1WithAttachments = database.GetDocumentWithIDAndRev(rev1.GetDocId
                                                                                        (), rev1.GetRevId(), contentOptions);
            // Map<String,Object> rev1PropertiesPrime = rev1WithAttachments.getProperties();
            // rev1PropertiesPrime.put("foo", 2);
            IDictionary <string, object> rev1WithAttachmentsProperties = rev1WithAttachments.GetProperties
                                                                             ();
            IDictionary <string, object> rev2Properties = new Dictionary <string, object>();

            rev2Properties.Put("_id", rev1WithAttachmentsProperties.Get("_id"));
            rev2Properties.Put("foo", 2);
            RevisionInternal newRev = new RevisionInternal(rev2Properties, database);
            RevisionInternal rev2   = database.PutRevision(newRev, rev1WithAttachments.GetRevId
                                                               (), false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            database.CopyAttachmentNamedFromSequenceToSequence(testAttachmentName, rev1WithAttachments
                                                               .GetSequence(), rev2.GetSequence());
            // Check the 2nd revision's attachment:
            Attachment rev2FetchedAttachment = database.GetAttachmentForSequence(rev2.GetSequence
                                                                                     (), testAttachmentName);

            NUnit.Framework.Assert.AreEqual(attachment.GetLength(), rev2FetchedAttachment.GetLength
                                                ());
            NUnit.Framework.Assert.AreEqual(attachment.GetMetadata(), rev2FetchedAttachment.GetMetadata
                                                ());
            NUnit.Framework.Assert.AreEqual(attachment.GetContentType(), rev2FetchedAttachment
                                            .GetContentType());
            // Add a third revision of the same document:
            IDictionary <string, object> rev3Properties = new Dictionary <string, object>();

            rev3Properties.Put("_id", rev2.GetProperties().Get("_id"));
            rev3Properties.Put("foo", 3);
            rev3Properties.Put("baz", false);
            RevisionInternal rev3 = new RevisionInternal(rev3Properties, database);

            rev3 = database.PutRevision(rev3, rev2.GetRevId(), false, status);
            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            byte[] attach3 = Sharpen.Runtime.GetBytesForString("<html><blink>attach3</blink></html>"
                                                               );
            database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach3
                                                                                         ), rev3.GetSequence(), testAttachmentName, "text/html", rev3.GetGeneration());
            // Check the 3rd revision's attachment:
            Attachment rev3FetchedAttachment = database.GetAttachmentForSequence(rev3.GetSequence
                                                                                     (), testAttachmentName);

            data = IOUtils.ToByteArray(rev3FetchedAttachment.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach3, data));
            NUnit.Framework.Assert.AreEqual("text/html", rev3FetchedAttachment.GetContentType
                                                ());
            // TODO: why doesn't this work?
            // Assert.assertEquals(attach3.length, rev3FetchedAttachment.getLength());
            ICollection <BlobKey> blobKeys = database.GetAttachments().AllKeys();

            NUnit.Framework.Assert.AreEqual(2, blobKeys.Count);
            database.Compact();
            blobKeys = database.GetAttachments().AllKeys();
            NUnit.Framework.Assert.AreEqual(1, blobKeys.Count);
        }
Exemple #18
0
 public void TearDown()
 {
     _store = null;
     Directory.Delete(_storePath, true);
 }
Exemple #19
0
        /// <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;

            try
            {
                database.UpdateAttachment(testAttachmentName, new ByteArrayInputStream(attachv2),
                                          "application/foo", rev1.GetDocId(), null);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == Status.Conflict);
            }
            NUnit.Framework.Assert.IsTrue(gotExpectedErrorCode);
            gotExpectedErrorCode = false;
            try
            {
                database.UpdateAttachment(testAttachmentName, new ByteArrayInputStream(attachv2),
                                          "application/foo", 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, new ByteArrayInputStream(attachv2
                                                                                              ), "application/foo", 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, 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, "nosuchdoc", "nosuchrev");
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == Status.NotFound);
            }
            NUnit.Framework.Assert.IsTrue(gotExpectedErrorCode);
            RevisionInternal rev3 = database.UpdateAttachment(testAttachmentName, null, null,
                                                              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 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;
        }
Exemple #21
0
        /// <exception cref="System.Exception"></exception>
        public virtual void TestAttachments()
        {
            string    testAttachmentName = "test_attachment";
            BlobStore attachments        = database.GetAttachments();

            NUnit.Framework.Assert.AreEqual(0, attachments.Count());
            NUnit.Framework.Assert.AreEqual(new HashSet <object>(), attachments.AllKeys());
            Status status = new Status();
            IDictionary <string, object> rev1Properties = new Dictionary <string, object>();

            rev1Properties.Put("foo", 1);
            rev1Properties.Put("bar", false);
            RevisionInternal rev1 = database.PutRevision(new RevisionInternal(rev1Properties,
                                                                              database), null, false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            byte[] attach1 = Sharpen.Runtime.GetBytesForString("This is the body of attach1");
            database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach1
                                                                                         ), rev1.GetSequence(), testAttachmentName, "text/plain", rev1.GetGeneration());
            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            Attachment attachment = database.GetAttachmentForSequence(rev1.GetSequence(), testAttachmentName
                                                                      );

            NUnit.Framework.Assert.AreEqual("text/plain", attachment.GetContentType());
            byte[] data = IOUtils.ToByteArray(attachment.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
            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> attachmentDict = new Dictionary <string, object>();

            attachmentDict.Put(testAttachmentName, innerDict);
            IDictionary <string, object> attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent
                                                                         (rev1.GetSequence(), EnumSet.NoneOf <Database.TDContentOptions>());

            NUnit.Framework.Assert.AreEqual(attachmentDict, attachmentDictForSequence);
            RevisionInternal gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1
                                                                        .GetRevId(), EnumSet.NoneOf <Database.TDContentOptions>());
            IDictionary <string, object> gotAttachmentDict = (IDictionary <string, object>)gotRev1
                                                             .GetProperties().Get("_attachments");

            NUnit.Framework.Assert.AreEqual(attachmentDict, gotAttachmentDict);
            // Check the attachment dict, with attachments included:
            Sharpen.Collections.Remove(innerDict, "stub");
            innerDict.Put("data", Base64.EncodeBytes(attach1));
            attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent(rev1
                                                                                          .GetSequence(), EnumSet.Of(Database.TDContentOptions.TDIncludeAttachments));
            NUnit.Framework.Assert.AreEqual(attachmentDict, attachmentDictForSequence);
            gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1.GetRevId(), EnumSet
                                                       .Of(Database.TDContentOptions.TDIncludeAttachments));
            gotAttachmentDict = (IDictionary <string, object>)gotRev1.GetProperties().Get("_attachments"
                                                                                          );
            NUnit.Framework.Assert.AreEqual(attachmentDict, gotAttachmentDict);
            // Add a second revision that doesn't update the attachment:
            IDictionary <string, object> rev2Properties = new Dictionary <string, object>();

            rev2Properties.Put("_id", rev1.GetDocId());
            rev2Properties.Put("foo", 2);
            rev2Properties.Put("bazz", false);
            RevisionInternal rev2 = database.PutRevision(new RevisionInternal(rev2Properties,
                                                                              database), rev1.GetRevId(), false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            database.CopyAttachmentNamedFromSequenceToSequence(testAttachmentName, rev1.GetSequence
                                                                   (), rev2.GetSequence());
            // Add a third revision of the same document:
            IDictionary <string, object> rev3Properties = new Dictionary <string, object>();

            rev3Properties.Put("_id", rev2.GetDocId());
            rev3Properties.Put("foo", 2);
            rev3Properties.Put("bazz", false);
            RevisionInternal rev3 = database.PutRevision(new RevisionInternal(rev3Properties,
                                                                              database), rev2.GetRevId(), false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            byte[] attach2 = Sharpen.Runtime.GetBytesForString("<html>And this is attach2</html>"
                                                               );
            database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach2
                                                                                         ), rev3.GetSequence(), testAttachmentName, "text/html", rev2.GetGeneration());
            // Check the 2nd revision's attachment:
            Attachment attachment2 = database.GetAttachmentForSequence(rev2.GetSequence(), testAttachmentName
                                                                       );

            NUnit.Framework.Assert.AreEqual("text/plain", attachment2.GetContentType());
            data = IOUtils.ToByteArray(attachment2.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
            // Check the 3rd revision's attachment:
            Attachment attachment3 = database.GetAttachmentForSequence(rev3.GetSequence(), testAttachmentName
                                                                       );

            NUnit.Framework.Assert.AreEqual("text/html", attachment3.GetContentType());
            data = IOUtils.ToByteArray(attachment3.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach2, data));
            // Examine the attachment store:
            NUnit.Framework.Assert.AreEqual(2, attachments.Count());
            ICollection <BlobKey> expected = new HashSet <BlobKey>();

            expected.AddItem(BlobStore.KeyForBlob(attach1));
            expected.AddItem(BlobStore.KeyForBlob(attach2));
            NUnit.Framework.Assert.AreEqual(expected, attachments.AllKeys());
            database.Compact();
            // This clears the body of the first revision
            NUnit.Framework.Assert.AreEqual(1, attachments.Count());
            ICollection <BlobKey> expected2 = new HashSet <BlobKey>();

            expected2.AddItem(BlobStore.KeyForBlob(attach2));
            NUnit.Framework.Assert.AreEqual(expected2, attachments.AllKeys());
        }
        public void TestAttachments()
        {
            var testAttachmentName = "test_attachment";
            var attachments        = database.Attachments;

            Assert.AreEqual(0, attachments.Count());
            Assert.AreEqual(0, attachments.AllKeys().Count());

            var rev1Properties = new Dictionary <string, object>();

            rev1Properties["foo"] = 1;
            rev1Properties["bar"] = false;

            var status = new Status();
            var rev1   = database.PutRevision(
                new RevisionInternal(rev1Properties), null, false, status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());

            var attach1 = Encoding.UTF8.GetBytes("This is the body of attach1");

            database.InsertAttachmentForSequenceWithNameAndType(
                new ByteArrayInputStream(attach1),
                rev1.GetSequence(),
                testAttachmentName,
                "text/plain",
                rev1.GetGeneration());

            //We must set the no_attachments column for the rev to false, as we are using an internal
            //private API call above (database.insertAttachmentForSequenceWithNameAndType) which does
            //not set the no_attachments column on revs table
            try
            {
                var args = new ContentValues();
                args.Put("no_attachments", false);
                database.StorageEngine.Update(
                    "revs",
                    args,
                    "sequence=?",
                    new[] { rev1.GetSequence().ToString() }
                    );
            }
            catch (SQLException e)
            {
                Log.E(Tag, "Error setting rev1 no_attachments to false", e);
                throw new CouchbaseLiteException(StatusCode.InternalServerError);
            }

            var attachment = database.GetAttachmentForSequence(
                rev1.GetSequence(),
                testAttachmentName
                );

            Assert.AreEqual("text/plain", attachment.ContentType);
            var data = attachment.Content.ToArray();

            Assert.IsTrue(Arrays.Equals(attach1, data));

            attachment.Dispose();

            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 attachmentDict = new Dictionary <string, object>();

            attachmentDict[testAttachmentName] = innerDict;
            var attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent(rev1.GetSequence(), DocumentContentOptions.None);

            Assert.AreEqual(new SortedDictionary <string, object>(attachmentDict), new SortedDictionary <string, object>(attachmentDictForSequence));//Assert.AreEqual(1, attachmentDictForSequence.Count);
            var gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(),
                                                           rev1.GetRevId(), DocumentContentOptions.IncludeAttachments);
            var gotAttachmentDict = gotRev1.GetProperties()
                                    .Get("_attachments")
                                    .AsDictionary <string, object>();

            Assert.AreEqual(attachmentDict.Select(kvp => kvp.Key).OrderBy(k => k), gotAttachmentDict.Select(kvp => kvp.Key).OrderBy(k => k));

            // Check the attachment dict, with attachments included:
            innerDict.Remove("stub");
            innerDict.Put("data", Convert.ToBase64String(attach1));
            attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent(
                rev1.GetSequence(), DocumentContentOptions.IncludeAttachments);
            Assert.AreEqual(new SortedDictionary <string, object>(attachmentDict[testAttachmentName].AsDictionary <string, object>()), new SortedDictionary <string, object>(attachmentDictForSequence[testAttachmentName].AsDictionary <string, object>()));

            gotRev1 = database.GetDocumentWithIDAndRev(
                rev1.GetDocId(), rev1.GetRevId(), DocumentContentOptions.IncludeAttachments);
            gotAttachmentDict = gotRev1.GetProperties()
                                .Get("_attachments")
                                .AsDictionary <string, object>()
                                .Get(testAttachmentName)
                                .AsDictionary <string, object>();
            Assert.AreEqual(innerDict.Select(kvp => kvp.Key).OrderBy(k => k), gotAttachmentDict.Select(kvp => kvp.Key).OrderBy(k => k));

            // Add a second revision that doesn't update the attachment:
            database.BeginTransaction();
            var rev2Properties = new Dictionary <string, object>();

            rev2Properties.Put("_id", rev1.GetDocId());
            rev2Properties["foo"]  = 2;
            rev2Properties["bazz"] = false;
            var rev2 = database.PutRevision(new RevisionInternal(rev2Properties), rev1.GetRevId(), false, status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());

            database.CopyAttachmentNamedFromSequenceToSequence(
                testAttachmentName, rev1.GetSequence(), rev2.GetSequence());
            database.EndTransaction(true);
            // Add a third revision of the same document:
            var rev3Properties = new Dictionary <string, object>();

            rev3Properties.Put("_id", rev2.GetDocId());
            rev3Properties["foo"]  = 2;
            rev3Properties["bazz"] = false;
            database.BeginTransaction();
            var rev3 = database.PutRevision(new RevisionInternal(
                                                rev3Properties), rev2.GetRevId(), false, status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());

            var attach2 = Encoding.UTF8.GetBytes("<html>And this is attach2</html>");

            database.InsertAttachmentForSequenceWithNameAndType(
                new ByteArrayInputStream(attach2), rev3.GetSequence(),
                testAttachmentName, "text/html", rev2.GetGeneration());
            database.EndTransaction(true);
            // Check the 2nd revision's attachment:
            var attachment2 = database.GetAttachmentForSequence(rev2.GetSequence(), testAttachmentName);

            Assert.AreEqual("text/plain", attachment2.ContentType);

            data = attachment2.Content.ToArray();
            Assert.IsTrue(Arrays.Equals(attach1, data));

            attachment2.Dispose();

            // Check the 3rd revision's attachment:
            var attachment3 = database.GetAttachmentForSequence(rev3.GetSequence(), testAttachmentName);

            Assert.AreEqual("text/html", attachment3.ContentType);

            data = attachment3.Content.ToArray();
            Assert.IsTrue(Arrays.Equals(attach2, data));

            var attachmentDictForRev3 = database.GetAttachmentsDictForSequenceWithContent(rev3.GetSequence(), DocumentContentOptions.None)
                                        .Get(testAttachmentName)
                                        .AsDictionary <string, object>();

            if (attachmentDictForRev3.ContainsKey("follows"))
            {
                if (((bool)attachmentDictForRev3.Get("follows")) == true)
                {
                    throw new RuntimeException("Did not expected attachment dict 'follows' key to be true"
                                               );
                }
                else
                {
                    throw new RuntimeException("Did not expected attachment dict to have 'follows' key"
                                               );
                }
            }

            attachment3.Dispose();

            // Examine the attachment store:
            Assert.AreEqual(2, attachments.Count());

            var expected = new HashSet <BlobKey>();

            expected.AddItem(BlobStore.KeyForBlob(attach1));
            expected.AddItem(BlobStore.KeyForBlob(attach2));
            Assert.AreEqual(expected.Count, attachments.AllKeys().Count());

            foreach (var key in attachments.AllKeys())
            {
                Assert.IsTrue(expected.Contains(key));
            }

            database.Compact();

            // This clears the body of the first revision
            Assert.AreEqual(1, attachments.Count());

            var expected2 = new HashSet <BlobKey>();

            expected2.AddItem(BlobStore.KeyForBlob(attach2));
            Assert.AreEqual(expected2.Count, attachments.AllKeys().Count());

            foreach (var key in attachments.AllKeys())
            {
                Assert.IsTrue(expected2.Contains(key));
            }
        }
Exemple #23
0
        internal void Open()
        {
            if (_isOpen) {
                return;
            }

            Log.D(TAG, "Opening {0}", Name);

            // Instantiate storage:
            //string storageType = Manager.StorageType ?? "SQLite";
            Storage = new SqliteCouchStore();
            Storage.Delegate = this;

            var encryptionKey = default(SymmetricKey);
            var gotKey = Manager.Shared.TryGetValue("encryptionKey", "", Name, out encryptionKey);
            if (gotKey) {
                Storage.SetEncryptionKey(encryptionKey);
            }

            Log.D(TAG, "Using {0} for db at {1}", Storage.GetType(), Path);
            try {
                Storage.Open(Path, Manager);

                // HACK: Needed to overcome the read connection not getting the write connection
                // changes until after the schema is written
                Storage.Close();
                Storage.Open(Path, Manager);
            } catch(CouchbaseLiteException) {
                Storage.Close();
                Log.W(TAG, "Error creating storage engine");
                throw;
            } catch(Exception e) {
                throw new CouchbaseLiteException("Unknown exception creating storage engine", e) { Code = StatusCode.Exception };
            }

            Storage.AutoCompact = AUTO_COMPACT;

            // First-time setup:
            if (PrivateUUID() == null) {
                Storage.SetInfo("privateUUID", Misc.CreateGUID());
                Storage.SetInfo("publicUUID", Misc.CreateGUID());
            }

            var savedMaxRevDepth = _maxRevTreeDepth != 0 ? _maxRevTreeDepth.ToString() : Storage.GetInfo("max_revs");
            int maxRevTreeDepth = 0;
            if (savedMaxRevDepth != null && int.TryParse(savedMaxRevDepth, out maxRevTreeDepth)) {
                MaxRevTreeDepth = maxRevTreeDepth;
            } else {
                MaxRevTreeDepth = DEFAULT_MAX_REVS;
            }

            // Open attachment store:
            string attachmentsPath = AttachmentStorePath;

            try {
                Attachments = new BlobStore(attachmentsPath, encryptionKey);
            } catch(CouchbaseLiteException) {
                Log.E(TAG, "Error creating blob store at {0}", attachmentsPath);
                Storage.Close();
                Storage = null;
                throw;
            } catch(Exception e) {
                Storage.Close();
                Storage = null;
                throw new CouchbaseLiteException(String.Format("Unknown error creating blob store at {0}", attachmentsPath),
                    e) { Code = StatusCode.Exception };
            }
           
            _isOpen = true;
        }
        internal void OpenWithOptions(DatabaseOptions options)
        {
            if (IsOpen) {
                return;
            }

            Log.D(TAG, "Opening {0}", Name);
            _readonly = _readonly || options.ReadOnly;

            // Instantiate storage:
            string storageType = options.StorageType ?? Manager.StorageType ?? DatabaseOptions.SQLITE_STORAGE;

            var className = String.Format("Couchbase.Lite.Store.{0}CouchStore", storageType);
            var primaryStorage = Type.GetType(className, false, true);
            var errorMessage = default(string);
            if (primaryStorage == null) {
                #if !FORESTDB
                if (storageType == DatabaseOptions.FORESTDB_STORAGE) {
                    errorMessage = "ForestDB storage option selected but not compiled into library";
                }
                #endif
                #if NOSQLITE
                if (storageType == DatabaseOptions.SQLITE_STORAGE) {
                    errorMessage = "SQLite storage option selected but not compiled into library";
                }
                #endif
            } else if (primaryStorage.GetInterface("Couchbase.Lite.Store.ICouchStore") == null) {
                errorMessage = String.Format("{0} does not implement ICouchStore", className);
                primaryStorage = null;
            }

            if (primaryStorage == null) {
                throw new CouchbaseLiteException(errorMessage, StatusCode.InvalidStorageType);
            }

            var upgrade = false;
            var primarySQLite = storageType == DatabaseOptions.SQLITE_STORAGE;
            var otherStorage = primarySQLite ? Type.GetType("Couchbase.Lite.Store.ForestDBCouchStore") : 
                Type.GetType("Couchbase.Lite.Store.SqliteCouchStore");


            var primaryStorageInstance = (ICouchStore)Activator.CreateInstance(primaryStorage);
            var otherStorageInstance = otherStorage != null ? (ICouchStore)Activator.CreateInstance(otherStorage) : null;
            if(options.StorageType != null) {
                // If explicit storage type given in options, always use primary storage type,
                // and if secondary db exists, try to upgrade from it:
                upgrade = otherStorageInstance != null && otherStorageInstance.DatabaseExistsIn(DbDirectory) &&
                    !primaryStorageInstance.DatabaseExistsIn(DbDirectory);

                if (upgrade && primarySQLite) {
                    throw new CouchbaseLiteException("Cannot upgrade to SQLite", StatusCode.InvalidStorageType);
                }
            } else {
                // If options don't specify, use primary unless secondary db already exists in dir:
                if (otherStorageInstance != null && otherStorageInstance.DatabaseExistsIn(DbDirectory)) {
                    primaryStorageInstance = otherStorageInstance;
                }
            }

            Log.I(TAG, "Using {0} for db at {1}; upgrade={2}", primaryStorage, DbDirectory, upgrade);
            Storage = primaryStorageInstance;
            Storage.Delegate = this;
            Storage.AutoCompact = AUTO_COMPACT;

            // Encryption:
            var encryptionKey = options.EncryptionKey;
            if (encryptionKey != null) {
                Storage.SetEncryptionKey(encryptionKey);
            }

            // Open the storage!
            try {
                Storage.Open(DbDirectory, Manager, _readonly);
            } catch(CouchbaseLiteException) {
                Storage.Close();
                Log.E(TAG, "Failed to open storage for database");
                throw;
            } catch(Exception e) {
                Storage.Close();
                throw new CouchbaseLiteException("Error opening storage for database", e);
            }

            // First-time setup:
            if (PrivateUUID() == null) {
                Storage.SetInfo("privateUUID", Misc.CreateGUID());
                Storage.SetInfo("publicUUID", Misc.CreateGUID());
            }

            var savedMaxRevDepth = _maxRevTreeDepth != 0 ? _maxRevTreeDepth.ToString() : Storage.GetInfo("max_revs");
            int maxRevTreeDepth = 0;
            if (savedMaxRevDepth != null && int.TryParse(savedMaxRevDepth, out maxRevTreeDepth)) {
                SetMaxRevTreeDepth(maxRevTreeDepth);
            } else {
                SetMaxRevTreeDepth(DEFAULT_MAX_REVS);
            }

            // Open attachment store:
            string attachmentsPath = AttachmentStorePath;

            try {
                Attachments = new BlobStore(attachmentsPath, encryptionKey);
            } catch(CouchbaseLiteException) {
                Log.E(TAG, "Error creating blob store at {0}", attachmentsPath);
                Storage.Close();
                Storage = null;
                throw;
            } catch(Exception e) {
                Storage.Close();
                Storage = null;
                throw new CouchbaseLiteException(String.Format("Unknown error creating blob store at {0}", attachmentsPath),
                    e) { Code = StatusCode.Exception };
            }

            IsOpen = true;

            if (upgrade) {
                var upgrader = DatabaseUpgraderFactory.CreateUpgrader(this, DbDirectory);
                try {
                    upgrader.Import();
                } catch(CouchbaseLiteException e) {
                    Log.W(TAG, "Upgrade failed for {0} (Status {1})", DbDirectory, e.CBLStatus);
                    upgrader.Backout();
                    Close();
                    throw;
                }
            }
        }
        internal void Open()
        {
            if (_isOpen) {
                return;
            }

            Log.D(TAG, "Opening {0}", Name);

            // Instantiate storage:
            string storageType = Manager.StorageType ?? "SQLite";
            #if !FORESTDB
            #if NOSQLITE
            #error No storage engine compilation options selected
            #endif
            if(storageType == "ForestDB") {
                throw new ApplicationException("ForestDB storage engine selected, but not compiled in library");
            }
            #elif FORESTDB
            #if NOSQLITE
            if(storageType == "SQLite") {
                throw new ApplicationException("SQLite storage engine selected, but not compiled in library");
            }
            #endif
            #endif

            var className = String.Format("Couchbase.Lite.Store.{0}CouchStore", storageType);
            var primaryStorage = Type.GetType(className, false, true);
            if(primaryStorage == null) {
                throw new InvalidOperationException(String.Format("'{0}' is not a valid storage type", storageType));
            }

            var isStore = primaryStorage.GetInterface("Couchbase.Lite.Store.ICouchStore") != null;
            if(!isStore) {
                throw new InvalidOperationException(String.Format("{0} does not implement ICouchStore", className));
            }

            #if !NOSQLITE
            #if FORESTDB
            var secondaryClass = "Couchbase.Lite.Store.ForestDBCouchStore";
            if(className == secondaryClass) {
                secondaryClass = "Couchbase.Lite.Store.SqliteCouchStore";
            }

            var secondaryStorage = Type.GetType(secondaryClass, false, true);
            Storage = (ICouchStore)Activator.CreateInstance(secondaryStorage);
            if(!Storage.DatabaseExistsIn(DbDirectory)) {
            #endif
                Storage = (ICouchStore)Activator.CreateInstance(primaryStorage);
            #if FORESTDB
            }
            #endif
            #else
            Storage = new ForestDBCouchStore();
            #endif

            var encryptionKey = default(SymmetricKey);
            var gotKey = Manager.Shared.TryGetValue("encryptionKey", "", Name, out encryptionKey);
            if (gotKey) {
                Storage.SetEncryptionKey(encryptionKey);
            }

            Storage.Delegate = this;
            Log.D(TAG, "Using {0} for db at {1}", Storage.GetType(), DbDirectory);
            try {
                Storage.Open(DbDirectory, Manager, false);

                // HACK: Needed to overcome the read connection not getting the write connection
                // changes until after the schema is written
                Storage.Close();
                Storage.Open(DbDirectory, Manager, false);
            } catch(CouchbaseLiteException) {
                Storage.Close();
                Log.W(TAG, "Failed to create storage engine");
                throw;
            } catch(Exception e) {
                throw new CouchbaseLiteException("Error creating storage engine", e) { Code = StatusCode.Exception };
            }

            Storage.AutoCompact = AUTO_COMPACT;

            // First-time setup:
            if (PrivateUUID() == null) {
                Storage.SetInfo("privateUUID", Misc.CreateGUID());
                Storage.SetInfo("publicUUID", Misc.CreateGUID());
            }

            var savedMaxRevDepth = _maxRevTreeDepth != 0 ? _maxRevTreeDepth.ToString() : Storage.GetInfo("max_revs");
            int maxRevTreeDepth = 0;
            if (savedMaxRevDepth != null && int.TryParse(savedMaxRevDepth, out maxRevTreeDepth)) {
                MaxRevTreeDepth = maxRevTreeDepth;
            } else {
                MaxRevTreeDepth = DEFAULT_MAX_REVS;
            }

            // Open attachment store:
            string attachmentsPath = AttachmentStorePath;

            try {
                Attachments = new BlobStore(attachmentsPath, encryptionKey);
            } catch(CouchbaseLiteException) {
                Log.E(TAG, "Error creating blob store at {0}", attachmentsPath);
                Storage.Close();
                Storage = null;
                throw;
            } catch(Exception e) {
                Storage.Close();
                Storage = null;
                throw new CouchbaseLiteException(String.Format("Unknown error creating blob store at {0}", attachmentsPath),
                    e) { Code = StatusCode.Exception };
            }
           
            _isOpen = true;
        }
        internal Boolean Open()
        {
            if (_isOpen)
            {
                return true;
            }

            // Create the storage engine.
            StorageEngine = SQLiteStorageEngineFactory.CreateStorageEngine();

            // Try to open the storage engine and stop if we fail.
            if (StorageEngine == null || !StorageEngine.Open(Path))
            {
                var msg = "Unable to create a storage engine, fatal error";
                Log.E(Tag, msg);
                throw new CouchbaseLiteException(msg);
            }

            // Stuff we need to initialize every time the sqliteDb opens:
            if (!Initialize("PRAGMA foreign_keys = ON;"))
            {
                Log.E(Tag, "Error turning on foreign keys");
                return false;
            }

            // Check the user_version number we last stored in the sqliteDb:
            var dbVersion = StorageEngine.GetVersion();

            // Incompatible version changes increment the hundreds' place:
            if (dbVersion >= 100)
            {
                Log.E(Tag, "Database: Database version (" + dbVersion + ") is newer than I know how to work with");
                StorageEngine.Close();
                return false;
            }

            if (dbVersion < 1)
            {
                // First-time initialization:
                // (Note: Declaring revs.sequence as AUTOINCREMENT means the values will always be
                // monotonically increasing, never reused. See <http://www.sqlite.org/autoinc.html>)
                if (!Initialize(Schema))
                {
                    StorageEngine.Close();
                    return false;
                }

                dbVersion = 3;
            }

            if (dbVersion < 2)
            {
                // Version 2: added attachments.revpos
                var upgradeSql = "ALTER TABLE attachments ADD COLUMN revpos INTEGER DEFAULT 0; PRAGMA user_version = 2";

                if (!Initialize(upgradeSql))
                {
                    StorageEngine.Close();
                    return false;
                }
                dbVersion = 2;
            }

            if (dbVersion < 3)
            {
                var upgradeSql = "CREATE TABLE localdocs ( " + "docid TEXT UNIQUE NOT NULL, " 
                                    + "revid TEXT NOT NULL, " + "json BLOB); " + "CREATE INDEX localdocs_by_docid ON localdocs(docid); "
                                    + "PRAGMA user_version = 3";
                if (!Initialize(upgradeSql))
                {
                    StorageEngine.Close();
                    return false;
                }
                dbVersion = 3;
            }

            if (dbVersion < 4)
            {
                var upgradeSql = "CREATE TABLE info ( " + "key TEXT PRIMARY KEY, " + "value TEXT); "
                                    + "INSERT INTO INFO (key, value) VALUES ('privateUUID', '" + Misc.CreateGUID(
                                       ) + "'); " + "INSERT INTO INFO (key, value) VALUES ('publicUUID',  '" + Misc.CreateGUID
                                    () + "'); " + "PRAGMA user_version = 4";
                if (!Initialize(upgradeSql))
                {
                    StorageEngine.Close();
                    return false;
                }
                dbVersion = 4;
            }
            if (dbVersion < 5)
            {
                // Version 5: added encoding for attachments
                var upgradeSql = "ALTER TABLE attachments ADD COLUMN encoding INTEGER DEFAULT 0; "
                    + "ALTER TABLE attachments ADD COLUMN encoded_length INTEGER; " + "PRAGMA user_version = 5";
                if (!Initialize(upgradeSql))
                {
                    StorageEngine.Close();
                    return false;
                }
                dbVersion = 5;
            }
            if (dbVersion < 6)
            {
                // Version 6: enable Write-Ahead Log (WAL) <http://sqlite.org/wal.html>
                // Not supported on Android, require SQLite 3.7.0
                //String upgradeSql  = "PRAGMA journal_mode=WAL; " +
                var upgradeSql = "PRAGMA user_version = 6";
                if (!Initialize(upgradeSql))
                {
                    StorageEngine.Close();
                    return false;
                }
                dbVersion = 6;
            }
            if (dbVersion < 7)
            {
                // Version 7: enable full-text search
                // Note: Apple's SQLite build does not support the icu or unicode61 tokenizers :(
                // OPT: Could add compress/decompress functions to make stored content smaller
                // Not supported on Android
                //String upgradeSql = "CREATE VIRTUAL TABLE fulltext USING fts4(content, tokenize=unicodesn); " +
                //"ALTER TABLE maps ADD COLUMN fulltext_id INTEGER; " +
                //"CREATE INDEX IF NOT EXISTS maps_by_fulltext ON maps(fulltext_id); " +
                //"CREATE TRIGGER del_fulltext DELETE ON maps WHEN old.fulltext_id not null " +
                //"BEGIN DELETE FROM fulltext WHERE rowid=old.fulltext_id| END; " +
                var upgradeSql = "PRAGMA user_version = 7";
                if (!Initialize(upgradeSql))
                {
                    StorageEngine.Close();
                    return false;
                }
                dbVersion = 7;
            }
            // (Version 8 was an older version of the geo index)
            if (dbVersion < 9)
            {
                // Version 9: Add geo-query index
                //String upgradeSql = "CREATE VIRTUAL TABLE bboxes USING rtree(rowid, x0, x1, y0, y1); " +
                //"ALTER TABLE maps ADD COLUMN bbox_id INTEGER; " +
                //"ALTER TABLE maps ADD COLUMN geokey BLOB; " +
                //"CREATE TRIGGER del_bbox DELETE ON maps WHEN old.bbox_id not null " +
                //"BEGIN DELETE FROM bboxes WHERE rowid=old.bbox_id| END; " +
                var upgradeSql = "PRAGMA user_version = 9";
                if (!Initialize(upgradeSql))
                {
                    StorageEngine.Close();
                    return false;
                }
                dbVersion = 9;
            }
            if (dbVersion < 10)
            {
                // Version 10: Add rev flag for whether it has an attachment
                var upgradeSql = "ALTER TABLE revs ADD COLUMN no_attachments BOOLEAN; " + "PRAGMA user_version = 10";
                if (!Initialize(upgradeSql))
                {
                    StorageEngine.Close();
                    return false;
                }
                dbVersion = 10;
            }
            if (dbVersion < 11)
            {
                // Version 10: Add another index
                var upgradeSql = "CREATE INDEX revs_cur_deleted ON revs(current,deleted); " + 
                    "PRAGMA user_version = 11";
                if (!Initialize(upgradeSql))
                {
                    StorageEngine.Close();
                    return false;
                }
            }

            try
            {
                Attachments = new BlobStore(AttachmentStorePath);
            }
            catch (ArgumentException e)
            {
                Log.E(Tag, "Could not initialize attachment store", e);
                StorageEngine.Close();
                return false;
            }

            _isOpen = true;

            return true;
        }
        public BlobStoreWriter(BlobStore store)
        {
            this.store = store;
            try
            {
                sha1Digest = MessageDigest.GetInstance("SHA-1");
                sha1Digest.Reset();
                md5Digest = MessageDigest.GetInstance("MD5");
                md5Digest.Reset();
            } catch (NotSupportedException e) {
                throw Misc.CreateExceptionAndLog(Log.To.Database, e, Tag,
                    "Could not get an instance of SHA-1 or MD5 for BlobStoreWriter.");
            }

            try {
                OpenTempFile();
            } catch (FileNotFoundException e) {
                throw Misc.CreateExceptionAndLog(Log.To.Database, e, Tag,
                    "Unable to open temporary file for BlobStoreWriter.");
            }
        }
Exemple #28
0
        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);
        }
        /// <exception cref="System.Exception"></exception>
        public virtual void TestAttachments()
        {
            string    testAttachmentName = "test_attachment";
            BlobStore attachments        = database.GetAttachments();

            NUnit.Framework.Assert.AreEqual(0, attachments.Count());
            NUnit.Framework.Assert.AreEqual(new HashSet <object>(), attachments.AllKeys());
            Status status = new Status();
            IDictionary <string, object> rev1Properties = new Dictionary <string, object>();

            rev1Properties.Put("foo", 1);
            rev1Properties.Put("bar", false);
            RevisionInternal rev1 = database.PutRevision(new RevisionInternal(rev1Properties,
                                                                              database), null, false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            byte[] attach1 = Sharpen.Runtime.GetBytesForString("This is the body of attach1");
            database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach1
                                                                                         ), rev1.GetSequence(), testAttachmentName, "text/plain", rev1.GetGeneration());
            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            //We must set the no_attachments column for the rev to false, as we are using an internal
            //private API call above (database.insertAttachmentForSequenceWithNameAndType) which does
            //not set the no_attachments column on revs table
            try
            {
                ContentValues args = new ContentValues();
                args.Put("no_attachments=", false);
                database.GetDatabase().Update("revs", args, "sequence=?", new string[] { rev1.GetSequence
                                                                                             ().ToString() });
            }
            catch (SQLException e)
            {
                Log.E(Database.Tag, "Error setting rev1 no_attachments to false", e);
                throw new CouchbaseLiteException(Status.InternalServerError);
            }
            Attachment attachment = database.GetAttachmentForSequence(rev1.GetSequence(), testAttachmentName
                                                                      );

            NUnit.Framework.Assert.AreEqual("text/plain", attachment.GetContentType());
            byte[] data = IOUtils.ToByteArray(attachment.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
            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> attachmentDict = new Dictionary <string, object>();

            attachmentDict.Put(testAttachmentName, innerDict);
            IDictionary <string, object> attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent
                                                                         (rev1.GetSequence(), EnumSet.NoneOf <Database.TDContentOptions>());

            NUnit.Framework.Assert.AreEqual(attachmentDict, attachmentDictForSequence);
            RevisionInternal gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1
                                                                        .GetRevId(), EnumSet.NoneOf <Database.TDContentOptions>());
            IDictionary <string, object> gotAttachmentDict = (IDictionary <string, object>)gotRev1
                                                             .GetProperties().Get("_attachments");

            NUnit.Framework.Assert.AreEqual(attachmentDict, gotAttachmentDict);
            // Check the attachment dict, with attachments included:
            Sharpen.Collections.Remove(innerDict, "stub");
            innerDict.Put("data", Base64.EncodeBytes(attach1));
            attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent(rev1
                                                                                          .GetSequence(), EnumSet.Of(Database.TDContentOptions.TDIncludeAttachments));
            NUnit.Framework.Assert.AreEqual(attachmentDict, attachmentDictForSequence);
            gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1.GetRevId(), EnumSet
                                                       .Of(Database.TDContentOptions.TDIncludeAttachments));
            gotAttachmentDict = (IDictionary <string, object>)gotRev1.GetProperties().Get("_attachments"
                                                                                          );
            NUnit.Framework.Assert.AreEqual(attachmentDict, gotAttachmentDict);
            // Add a second revision that doesn't update the attachment:
            IDictionary <string, object> rev2Properties = new Dictionary <string, object>();

            rev2Properties.Put("_id", rev1.GetDocId());
            rev2Properties.Put("foo", 2);
            rev2Properties.Put("bazz", false);
            RevisionInternal rev2 = database.PutRevision(new RevisionInternal(rev2Properties,
                                                                              database), rev1.GetRevId(), false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            database.CopyAttachmentNamedFromSequenceToSequence(testAttachmentName, rev1.GetSequence
                                                                   (), rev2.GetSequence());
            // Add a third revision of the same document:
            IDictionary <string, object> rev3Properties = new Dictionary <string, object>();

            rev3Properties.Put("_id", rev2.GetDocId());
            rev3Properties.Put("foo", 2);
            rev3Properties.Put("bazz", false);
            RevisionInternal rev3 = database.PutRevision(new RevisionInternal(rev3Properties,
                                                                              database), rev2.GetRevId(), false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            byte[] attach2 = Sharpen.Runtime.GetBytesForString("<html>And this is attach2</html>"
                                                               );
            database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach2
                                                                                         ), rev3.GetSequence(), testAttachmentName, "text/html", rev2.GetGeneration());
            // Check the 2nd revision's attachment:
            Attachment attachment2 = database.GetAttachmentForSequence(rev2.GetSequence(), testAttachmentName
                                                                       );

            NUnit.Framework.Assert.AreEqual("text/plain", attachment2.GetContentType());
            data = IOUtils.ToByteArray(attachment2.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
            // Check the 3rd revision's attachment:
            Attachment attachment3 = database.GetAttachmentForSequence(rev3.GetSequence(), testAttachmentName
                                                                       );

            NUnit.Framework.Assert.AreEqual("text/html", attachment3.GetContentType());
            data = IOUtils.ToByteArray(attachment3.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach2, data));
            IDictionary <string, object> attachmentDictForRev3 = (IDictionary <string, object>)
                                                                 database.GetAttachmentsDictForSequenceWithContent(rev3.GetSequence(), EnumSet.NoneOf
                                                                                                                   <Database.TDContentOptions>()).Get(testAttachmentName);

            if (attachmentDictForRev3.ContainsKey("follows"))
            {
                if (((bool)attachmentDictForRev3.Get("follows")) == true)
                {
                    throw new RuntimeException("Did not expected attachment dict 'follows' key to be true"
                                               );
                }
                else
                {
                    throw new RuntimeException("Did not expected attachment dict to have 'follows' key"
                                               );
                }
            }
            // Examine the attachment store:
            NUnit.Framework.Assert.AreEqual(2, attachments.Count());
            ICollection <BlobKey> expected = new HashSet <BlobKey>();

            expected.AddItem(BlobStore.KeyForBlob(attach1));
            expected.AddItem(BlobStore.KeyForBlob(attach2));
            NUnit.Framework.Assert.AreEqual(expected, attachments.AllKeys());
            database.Compact();
            // This clears the body of the first revision
            NUnit.Framework.Assert.AreEqual(1, attachments.Count());
            ICollection <BlobKey> expected2 = new HashSet <BlobKey>();

            expected2.AddItem(BlobStore.KeyForBlob(attach2));
            NUnit.Framework.Assert.AreEqual(expected2, attachments.AllKeys());
        }