// This is ONLY FOR TESTS
        internal BlobStoreWriter AttachmentWriterForAttachment(IDictionary<string, object> attachment)
        {
            var digest = attachment.GetCast<string>("digest");
            if (digest == null) {
                return null;
            }

            return _pendingAttachmentsByDigest.Get(digest);
        }
 internal static RevisionID CblRev(this IDictionary <string, object> dict)
 {
     return(dict?.GetCast <string>("_rev").AsRevID());
 }
        internal RevisionInternal PutDocument(string docId, IDictionary<string, object> properties, string prevRevId, bool allowConflict)
        {
            bool deleting = properties == null || properties.GetCast<bool>("_deleted");
            Log.D(TAG, "PUT _id={0}, _rev={1}, _deleted={2}, allowConflict={3}", docId, prevRevId, deleting, allowConflict);
            if ((prevRevId != null && docId == null) || (deleting && docId == null)) {
                throw new CouchbaseLiteException(StatusCode.BadId);
            }

            if (properties != null && properties.Get("_attachments").AsDictionary<string, object>() != null) {
                var tmpRev = new RevisionInternal(docId, prevRevId, deleting);
                tmpRev.SetProperties(properties);
                if (!ProcessAttachmentsForRevision(tmpRev, prevRevId == null ? null : new List<string> { prevRevId })) {
                    return null;
                }

                properties = tmpRev.GetProperties();
            }

            StoreValidation validationBlock = null;
            if (Shared.HasValues("validation", Name)) {
                validationBlock = ValidateRevision;
            }

            var putRev = Storage.PutRevision(docId, prevRevId, properties, deleting, allowConflict, validationBlock);
            if (putRev != null) {
                Log.D(TAG, "--> created {0}", putRev);
                if (!string.IsNullOrEmpty(docId)) {
                    var dummy = default(WeakReference);
                    UnsavedRevisionDocumentCache.TryRemove(docId, out dummy);
                }
            }

            return putRev;
        }
        internal RevisionInternal PutDocument(string docId, IDictionary<string, object> properties, RevisionID prevRevId, bool allowConflict, Uri source)
        {
            bool deleting = properties == null || properties.GetCast<bool>("_deleted");
            Log.To.Database.V(TAG, "PUT _id={0}, _rev={1}, _deleted={2}, allowConflict={3}",
                new SecureLogString(docId, LogMessageSensitivity.PotentiallyInsecure), prevRevId, deleting, allowConflict);
            if(prevRevId != null && docId == null) {
                throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadId, TAG,
                    "prevRevId {0} specified in PutDocument, but docId not specified", prevRevId);
            }

            if(deleting && docId == null) {
                throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadId, TAG,
                    "No document ID specified on a delete request");
            }

            if(properties != null && properties.Get("_attachments").AsDictionary<string, object>() != null) {
                var generation = prevRevId == null ? 1 : prevRevId.Generation + 1;
                var tmpRevID = String.Format("{0}-00", generation).AsRevID();
                var tmpRev = new RevisionInternal(docId ?? "x", tmpRevID, deleting);
                tmpRev.SetProperties(properties);
                if(!ProcessAttachmentsForRevision(tmpRev, prevRevId == null ? null : new List<RevisionID> { prevRevId })) {
                    return null;
                }

                properties = tmpRev.GetProperties();
            }

            StoreValidation validationBlock = null;
            if(Shared.HasValues("validation", Name)) {
                validationBlock = ValidateRevision;
            }

            return Storage.PutRevision(docId, prevRevId, properties, deleting, allowConflict, source, validationBlock);
        }
 internal static string CblID(this IDictionary <string, object> dict)
 {
     return(dict?.GetCast <string>("_id"));
 }
        public AttachmentInternal(string name, IDictionary<string, object> info) 
            : this(name, info.GetCast<string>("content_type"))
        {
            Length = info.GetCast<long>("length");
            EncodedLength = info.GetCast<long>("encoded_length");
            _digest = info.GetCast<string>("digest");
            if (_digest != null) {
                BlobKey = new BlobKey(_digest);
            }

            string encodingString = info.GetCast<string>("encoding");
            if (encodingString != null) {
                if (encodingString.Equals("gzip")) {
                    Encoding = AttachmentEncoding.GZIP;
                } else {
                    throw new CouchbaseLiteException(StatusCode.BadEncoding);
                }
            }

            var data = info.Get("data");
            if (data != null) {
                // If there's inline attachment data, decode and store it:
                if (data is string) {
                    _data = Convert.FromBase64String((string)data);
                } else {
                    _data = data as IEnumerable<byte>;
                }

                if (_data == null) {
                    throw new CouchbaseLiteException(StatusCode.BadEncoding);
                }

                SetPossiblyEncodedLength(_data.LongCount());
            } else if (info.GetCast<bool>("stub", false)) {
                // This item is just a stub; validate and skip it
                if(info.ContainsKey("revpos")) {
                    var revPos = info.GetCast<int>("revpos");
                    if (revPos <= 0) {
                        throw new CouchbaseLiteException(StatusCode.BadAttachment);
                    }

                    RevPos = revPos;
                }
            } else if (info.GetCast<bool>("follows", false)) {
                // I can't handle this myself; my caller will look it up from the digest
                if (Digest == null) {
                    throw new CouchbaseLiteException(StatusCode.BadAttachment);
                }

                if(info.ContainsKey("revpos")) {
                    var revPos = info.GetCast<int>("revpos");
                    if (revPos <= 0) {
                        throw new CouchbaseLiteException(StatusCode.BadAttachment);
                    }

                    RevPos = revPos;
                }
            } else {
                throw new CouchbaseLiteException(StatusCode.BadAttachment);
            }
        }
        private static long SmallestLength(IDictionary<string, object> attachment)
        {
            long length = attachment.GetCast<long>("length");
            long encodedLength = attachment.GetCast<long>("encoded_length", -1);
            if (encodedLength != -1) {
                length = encodedLength;
            }

            return length;
        }
        /// <summary>
        /// Gets the status from a response from _bulk_docs and translates it into
        /// a Status object
        /// </summary>
        /// <returns>The status of the request</returns>
        /// <param name="item">The response received</param>
        protected Status StatusFromBulkDocsResponseItem(IDictionary<string, object> item)
        {
            try {
                if (!item.ContainsKey("error")) {
                    return new Status(StatusCode.Ok);
                }

                var errorStr = item.Get("error") as string;
                if (StringEx.IsNullOrWhiteSpace(errorStr)) {
                    return new Status(StatusCode.Ok);
                }

                // 'status' property is nonstandard; TouchDB returns it, others don't.
                var status = item.GetCast<int>("status");
                if (status >= 400) {
                    return new Status((StatusCode)status);
                }

                // If no 'status' present, interpret magic hardcoded CouchDB error strings:
                if (errorStr.Equals("unauthorized", StringComparison.InvariantCultureIgnoreCase)) {
                    return new Status(StatusCode.Unauthorized);
                } else {
                    if (errorStr.Equals("forbidden", StringComparison.InvariantCultureIgnoreCase)) {
                        return new Status(StatusCode.Forbidden);
                    } else {
                        if (errorStr.Equals("conflict", StringComparison.InvariantCultureIgnoreCase)) {
                            return new Status(StatusCode.Conflict);
                        } else {
                            return new Status(StatusCode.UpStreamError);
                        }
                    }
                }
            } catch (Exception e) {
                Log.E(Database.TAG, "Exception getting status from " + item, e);
            }

            return new Status(StatusCode.Ok);
        }
Exemple #9
0
        private Status ParseReplicationProperties(IDictionary <string, object> properties, out bool isPush, out bool createTarget, IDictionary <string, object> results)
        {
            // http://wiki.apache.org/couchdb/Replication
            isPush       = false;
            createTarget = false;
            var sourceDict = ParseSourceOrTarget(properties, "source");
            var targetDict = ParseSourceOrTarget(properties, "target");
            var source     = sourceDict.GetCast <string>("url");
            var target     = targetDict.GetCast <string>("url");

            if (source == null || target == null)
            {
                return(new Status(StatusCode.BadRequest));
            }

            createTarget = properties.GetCast <bool>("create_target", false);
            IDictionary <string, object> remoteDict = null;
            bool targetIsLocal = Manager.IsValidDatabaseName(target);

            if (Manager.IsValidDatabaseName(source))
            {
                //Push replication
                if (targetIsLocal)
                {
                    // This is a local-to-local replication. It is not supported on .NET.
                    return(new Status(StatusCode.NotImplemented));
                }

                remoteDict = targetDict;
                if (results.ContainsKey("database"))
                {
                    results["database"] = GetExistingDatabase(source);
                }

                isPush = true;
            }
            else if (targetIsLocal)
            {
                //Pull replication
                remoteDict = sourceDict;
                if (results.ContainsKey("database"))
                {
                    Database db;
                    if (createTarget)
                    {
                        db = GetDatabase(target);
                        if (db == null)
                        {
                            return(new Status(StatusCode.DbError));
                        }
                    }
                    else
                    {
                        db = GetExistingDatabase(target);
                    }
                    results["database"] = db;
                }
            }
            else
            {
                return(new Status(StatusCode.BadId));
            }

            Uri remote;

            if (!Uri.TryCreate(remoteDict.GetCast <string>("url"), UriKind.Absolute, out remote))
            {
                Log.To.Router.W(TAG, "Unparseable replication URL <{0}> received", remoteDict.GetCast <string>("url"));
                return(new Status(StatusCode.BadRequest));
            }

            if (!remote.Scheme.Equals("http") && !remote.Scheme.Equals("https") && !remote.Scheme.Equals("ws") && !remote.Scheme.Equals("wss"))
            {
                Log.To.Router.W(TAG, "Replication URL <{0}> has unsupported scheme", remote);
                return(new Status(StatusCode.BadRequest));
            }

            var split = remote.PathAndQuery.Split('?');

            if (split.Length != 1)
            {
                Log.To.Router.W(TAG, "Replication URL <{0}> must not contain a query", remote);
                return(new Status(StatusCode.BadRequest));
            }

            var path = split[0];

            if (String.IsNullOrEmpty(path) || path == "/")
            {
                Log.To.Router.W(TAG, "Replication URL <{0}> missing database name", remote);
                return(new Status(StatusCode.BadRequest));
            }

            var database = results.Get("database");

            if (database == null)
            {
                return(new Status(StatusCode.NotFound));
            }

            if (results.ContainsKey("remote"))
            {
                results["remote"] = remote;
            }

            if (results.ContainsKey("headers"))
            {
                results["headers"] = remoteDict.Get("headers") ?? new Dictionary <string, string>();
            }

            if (results.ContainsKey("authorizer"))
            {
                var auth = remoteDict.Get("auth") as IDictionary <string, object>;
                if (auth != null)
                {
                    var persona  = auth.Get("persona") as IDictionary <string, object>;
                    var facebook = auth.Get("facebook") as IDictionary <string, object>;
                    if (persona != null)
                    {
                        string email = persona.Get("email") as string;
                        results["authorizer"] = new PersonaAuthorizer(email);
                    }
                    else if (facebook != null)
                    {
                        string email = facebook.Get("email") as string;
                        results["authorizer"] = new FacebookAuthorizer(email);
                    }
                    else
                    {
                        Log.To.Sync.W(TAG, "Invalid authorizer settings {0}",
                                      new SecureLogJsonString(auth, LogMessageSensitivity.Insecure));
                    }
                }
            }



            // Can't specify both a filter and doc IDs
            if (properties.ContainsKey("filter") && properties.ContainsKey("doc_ids"))
            {
                return(new Status(StatusCode.BadRequest));
            }

            return(new Status(StatusCode.Ok));
        }
        public AttachmentInternal(string name, IDictionary <string, object> info)
            : this(name, info.GetCast <string>("content_type"))
        {
            Length        = info.GetCast <long>("length");
            EncodedLength = info.GetCast <long>("encoded_length");
            _digest       = info.GetCast <string>("digest");
            if (_digest != null)
            {
                BlobKey = new BlobKey(_digest);
            }

            string encodingString = info.GetCast <string>("encoding");

            if (encodingString != null)
            {
                if (encodingString.Equals("gzip"))
                {
                    Encoding = AttachmentEncoding.GZIP;
                }
                else
                {
                    throw new CouchbaseLiteException(StatusCode.BadEncoding);
                }
            }

            var data = info.Get("data");

            if (data != null)
            {
                // If there's inline attachment data, decode and store it:
                if (data is string)
                {
                    _data = Convert.FromBase64String((string)data);
                }
                else
                {
                    _data = data as IEnumerable <byte>;
                }

                if (_data == null)
                {
                    throw new CouchbaseLiteException(StatusCode.BadEncoding);
                }

                SetPossiblyEncodedLength(_data.LongCount());
            }
            else if (info.GetCast <bool>("stub", false))
            {
                // This item is just a stub; validate and skip it
                if (info.ContainsKey("revpos"))
                {
                    var revPos = info.GetCast <int>("revpos");
                    if (revPos <= 0)
                    {
                        throw new CouchbaseLiteException(StatusCode.BadAttachment);
                    }

                    RevPos = revPos;
                }
            }
            else if (info.GetCast <bool>("follows", false))
            {
                // I can't handle this myself; my caller will look it up from the digest
                if (Digest == null)
                {
                    throw new CouchbaseLiteException(StatusCode.BadAttachment);
                }

                if (info.ContainsKey("revpos"))
                {
                    var revPos = info.GetCast <int>("revpos");
                    if (revPos <= 0)
                    {
                        throw new CouchbaseLiteException(StatusCode.BadAttachment);
                    }

                    RevPos = revPos;
                }
            }
            else
            {
                throw new CouchbaseLiteException(StatusCode.BadAttachment);
            }
        }
Exemple #11
0
        public void ChangeTrackerReceivedChange(IDictionary <string, object> change)
        {
            if (ServerType == null)
            {
                ServerType = _changeTracker.ServerType;
            }

            var lastSequence = change.Get("seq").ToString();
            var docID        = (string)change.Get("id");

            if (docID == null)
            {
                return;
            }

            var removed = change.Get("removed") != null;

            if (removed)
            {
                return;
            }

            if (!Document.IsValidDocumentId(docID))
            {
                if (!docID.StartsWith("_user/", StringComparison.InvariantCultureIgnoreCase))
                {
                    Log.To.Sync.W(TAG, "{0}: Received invalid doc ID from _changes: {1} ({2})",
                                  this, new SecureLogString(docID, LogMessageSensitivity.PotentiallyInsecure), new LogJsonString(change));
                }

                return;
            }

            var deleted = change.GetCast <bool>("deleted");
            var changes = change.Get("changes").AsList <object>();

            SafeAddToChangesCount(changes.Count);

            foreach (var changeObj in changes)
            {
                var changeDict = changeObj.AsDictionary <string, object>();
                var revID      = changeDict.GetCast <string>("rev");
                if (revID == null)
                {
                    continue;
                }

                var rev = new PulledRevision(docID, revID, deleted, LocalDatabase);
                rev.SetRemoteSequenceID(lastSequence);
                if (changes.Count > 1)
                {
                    rev.IsConflicted = true;
                }

                Log.To.Sync.D(TAG, "Adding rev to inbox " + rev);
                AddToInbox(rev);
            }

            PauseOrResume();

            while (_revsToPull != null && _revsToPull.Count > 1000)
            {
                try {
                    // Presumably we are letting 1 or more other threads do something while we wait.
                    Thread.Sleep(500);
                }
                catch (Exception e) {
                    Log.To.Sync.W(TAG, "Swallowing exception while sleeping after receiving changetracker changes.", e);
                    // swallow
                }
            }
        }
Exemple #12
0
        public AttachmentInternal(string name, IDictionary <string, object> info)
            : this(name, info.GetCast <string>("content_type"))
        {
            Length        = info.GetCast <long>("length");
            EncodedLength = info.GetCast <long>("encoded_length");
            _digest       = info.GetCast <string>("digest");
            if (_digest != null)
            {
                BlobKey = new BlobKey(_digest);
            }

            string encodingString = info.GetCast <string>("encoding");

            if (encodingString != null)
            {
                if (encodingString.Equals("gzip"))
                {
                    Encoding = AttachmentEncoding.GZIP;
                }
                else
                {
                    throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadEncoding, TAG,
                                                     "Invalid encoding type ({0}) in ctor", encodingString);
                }
            }

            var data = info.Get("data");

            if (data != null)
            {
                // If there's inline attachment data, decode and store it:
                if (data is string)
                {
                    _data = Convert.FromBase64String((string)data);
                }
                else
                {
                    _data = data as IEnumerable <byte>;
                }

                if (_data == null)
                {
                    throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadEncoding, TAG,
                                                     "Invalid data type ({0}) in ctor", data.GetType().Name);
                }

                SetPossiblyEncodedLength(_data.LongCount());
            }
            else if (info.GetCast <bool>("stub", false))
            {
                // This item is just a stub; validate and skip it
                if (info.ContainsKey("revpos"))
                {
                    var revPos = info.GetCast("revpos", -1);  // PouchDB has a bug that generates "revpos":0; allow this (#627)
                    if (revPos < 0)
                    {
                        throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadAttachment, TAG,
                                                         "Invalid revpos ({0}) in ctor", revPos);
                    }

                    RevPos = revPos;
                }
            }
            else if (info.GetCast <bool>("follows", false))
            {
                // I can't handle this myself; my caller will look it up from the digest
                if (Digest == null)
                {
                    throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadAttachment, TAG,
                                                     "follows is true, but the attachment digest is null in ctor");
                }

                if (info.ContainsKey("revpos"))
                {
                    var revPos = info.GetCast("revpos", -1);  // PouchDB has a bug that generates "revpos":0; allow this (#627)
                    if (revPos < 0)
                    {
                        throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadAttachment, TAG,
                                                         "Invalid revpos ({0}) in ctor", revPos);
                    }

                    RevPos = revPos;
                }
            }
            else
            {
                throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadAttachment, TAG,
                                                 "Neither data nor stub nor follows was specified on the attachment data");
            }
        }
        public IEnumerable <QueryRow> RegularQuery(QueryOptions options)
        {
            var db     = _dbStorage;
            var filter = options.Filter;
            int limit  = int.MaxValue;
            int skip   = 0;

            if (filter != null)
            {
                // Custom post-filter means skip/limit apply to the filtered rows, not to the
                // underlying query, so handle them specially:
                limit         = options.Limit;
                skip          = options.Skip;
                options.Limit = QueryOptions.DEFAULT_LIMIT;
                options.Skip  = 0;
            }

            var rows = new List <QueryRow>();

            RunQuery(options, (keyData, valueData, docId, cursor) =>
            {
                long sequence = cursor.GetLong(3);
                RevisionInternal docRevision = null;
                if (options.IncludeDocs)
                {
                    IDictionary <string, object> value = null;
                    if (valueData != null && !RowValueIsEntireDoc(valueData.Value))
                    {
                        value = valueData.Value.AsDictionary <string, object>();
                    }

                    string linkedId = value == null ? null : value.GetCast <string>("_id");
                    if (linkedId != null)
                    {
                        // Linked document: http://wiki.apache.org/couchdb/Introduction_to_CouchDB_views#Linked_documents
                        string linkedRev = value == null ? null : value.GetCast <string>("_rev"); //usually null
                        docRevision      = db.GetDocument(linkedId, linkedRev, true);
                        sequence         = docRevision == null ? 0 : docRevision.GetSequence();
                    }
                    else
                    {
                        docRevision = db.GetRevision(docId, cursor.GetString(4), false, sequence, cursor.GetBlob(5));
                    }
                }

                Log.V(TAG, "Query {0}: Found row with key={1}, value={2}, id={3}",
                      Name, keyData.Value, valueData.Value, docId);

                QueryRow row = null;
                if (false)
                {
                    //TODO: bbox
                }
                else
                {
                    row = new QueryRow(docId, sequence, keyData.Value, valueData.Value, docRevision, this);
                }

                if (filter != null)
                {
                    if (!filter(row))
                    {
                        return(new Status(StatusCode.Ok));
                    }

                    if (skip > 0)
                    {
                        --skip;
                        return(new Status(StatusCode.Ok));
                    }
                }

                rows.Add(row);
                if (limit-- == 0)
                {
                    return(new Status(StatusCode.Reserved));
                }

                return(new Status(StatusCode.Ok));
            });

            // If given keys, sort the output into that order, and add entries for missing keys:
            if (options.Keys != null)
            {
                // Group rows by key:
                var rowsByKey = new Dictionary <object, List <QueryRow> >();
                foreach (var row in rows)
                {
                    var dictRows = rowsByKey.Get(row.Key);
                    if (dictRows == null)
                    {
                        dictRows = rowsByKey[row.Key] = new List <QueryRow>();
                    }

                    dictRows.Add(row);
                }

                // Now concatenate them in the order the keys are given in options:
                var sortedRows = new List <QueryRow>();
                foreach (var key in options.Keys)
                {
                    var dictRows = rowsByKey.Get(key);
                    if (dictRows != null)
                    {
                        sortedRows.AddRange(dictRows);
                    }
                }

                rows = sortedRows;
            }

            return(rows);
        }
        public static IList<RevisionID> ParseRevisionHistoryDict(IDictionary<string, object> dict)
        {
            if(dict == null) {
                return null;
            }

            // Extract the history, expanding the numeric prefixes
            var start = dict.GetCast<int>("start");
            var revIDs = dict.Get("ids").AsList<string>();
            return revIDs?.Select(x =>
            {
                var str = x as string;
                if(str == null) {
                    return null;
                }

                if(start > 0) {
                    str = String.Format("{0}-{1}", start--, str);
                }

                return str.AsRevID();
            })?.ToList();
        }
        public AttachmentInternal(string name, IDictionary<string, object> info) 
            : this(name, info.GetCast<string>("content_type"))
        {
            Length = info.GetCast<long>("length");
            EncodedLength = info.GetCast<long>("encoded_length");
            _digest = info.GetCast<string>("digest");
            if (_digest != null) {
                BlobKey = new BlobKey(_digest);
            }

            string encodingString = info.GetCast<string>("encoding");
            if (encodingString != null) {
                if (encodingString.Equals("gzip")) {
                    Encoding = AttachmentEncoding.GZIP;
                } else {
                    throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadEncoding, TAG,
                        "Invalid encoding type ({0}) in ctor", encodingString);
                }
            }

            var data = info.Get("data");
            if (data != null) {
                // If there's inline attachment data, decode and store it:
                if (data is string) {
                    _data = Convert.FromBase64String((string)data);
                } else {
                    _data = data as IEnumerable<byte>;
                }

                if (_data == null) {
                    throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadEncoding, TAG,
                        "Invalid data type ({0}) in ctor", data.GetType().Name);
                }

                SetPossiblyEncodedLength(_data.LongCount());
            } else if (info.GetCast<bool>("stub", false)) {
                // This item is just a stub; validate and skip it
                if(info.ContainsKey("revpos")) {
                    var revPos = info.GetCast("revpos", -1);  // PouchDB has a bug that generates "revpos":0; allow this (#627)
                    if (revPos < 0) {
                        throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadAttachment, TAG,
                            "Invalid revpos ({0}) in ctor", revPos);
                    }

                    RevPos = revPos;
                }
            } else if (info.GetCast<bool>("follows", false)) {
                // I can't handle this myself; my caller will look it up from the digest
                if (Digest == null) {
                    throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadAttachment, TAG,
                        "follows is true, but the attachment digest is null in ctor");
                }

                if(info.ContainsKey("revpos")) {
                    var revPos = info.GetCast("revpos", -1);  // PouchDB has a bug that generates "revpos":0; allow this (#627)
                    if (revPos < 0) {
                        throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadAttachment, TAG,
                            "Invalid revpos ({0}) in ctor", revPos);
                    }

                    RevPos = revPos;
                }
            } else {
                throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadAttachment, TAG,
                    "Neither data nor stub nor follows was specified on the attachment data");
            }
        }
 public static T GetCast <T>(this IDictionary <string, object> collection, string key)
 {
     return(collection.GetCast(key, default(T)));
 }
        private bool SaveDocument(C4Document *doc, string revId, IDictionary<string, object> properties)
        {
            // Is the new revision the winner?
            Native.c4doc_selectCurrentRevision(doc);
            bool isWinner = (string)doc->selectedRev.revID == revId;

            // Update the documentType:
            if (isWinner) {
                var type = properties == null ? null : properties.GetCast<string>("type");
                ForestDBBridge.Check(err => Native.c4doc_setType(doc, type, err));
            }

            // Save:
            ForestDBBridge.Check(err => Native.c4doc_save(doc, (uint)MaxRevTreeDepth, err));
            return isWinner;
        }
Exemple #18
0
        private void UploadChanges(IList <RevisionInternal> changes, IDictionary <string, object> revsDiffResults)
        {
            // Go through the list of local changes again, selecting the ones the destination server
            // said were missing and mapping them to a JSON dictionary in the form _bulk_docs wants:
            var docsToSend = new List <object> ();
            var revsToSend = new RevisionList();
            IDictionary <string, object> revResults = null;

            foreach (var rev in changes)
            {
                // Is this revision in the server's 'missing' list?
                if (revsDiffResults != null)
                {
                    revResults = revsDiffResults.Get(rev.DocID).AsDictionary <string, object>();
                    if (revResults == null)
                    {
                        continue;
                    }

                    var revs = revResults.Get("missing").AsList <string>();
                    if (revs == null || !revs.Any(id => id.Equals(rev.RevID.ToString())))
                    {
                        RemovePending(rev);
                        continue;
                    }
                }

                IDictionary <string, object> properties = null;
                RevisionInternal             loadedRev;
                try {
                    loadedRev = LocalDatabase.LoadRevisionBody(rev);
                    if (loadedRev == null)
                    {
                        throw Misc.CreateExceptionAndLog(Log.To.Sync, StatusCode.NotFound, TAG,
                                                         "Unable to load revision body");
                    }

                    properties = rev.GetProperties();
                } catch (Exception e1) {
                    Log.To.Sync.E(TAG, String.Format("Couldn't get local contents of {0}, marking revision failed",
                                                     rev), e1);
                    RevisionFailed();
                    continue;
                }

                if (properties.GetCast <bool> ("_removed"))
                {
                    RemovePending(rev);
                    continue;
                }

                var populatedRev = TransformRevision(loadedRev);
                var backTo       = revResults?.Get("possible_ancestors")?.AsList <RevisionID>();

                try {
                    var history = LocalDatabase.GetRevisionHistory(populatedRev, backTo);
                    if (history == null)
                    {
                        throw Misc.CreateExceptionAndLog(Log.To.Sync, StatusCode.DbError, TAG,
                                                         "Unable to load revision history");
                    }

                    properties["_revisions"] = TreeRevisionID.MakeRevisionHistoryDict(history);
                    populatedRev.SetPropertyForKey("_revisions", properties["_revisions"]);
                } catch (Exception e1) {
                    Log.To.Sync.E(TAG, "Error getting revision history, marking revision failed", e1);
                    RevisionFailed();
                    continue;
                }

                // Strip any attachments already known to the target db:
                if (properties.Get("_attachments") != null)
                {
                    // Look for the latest common ancestor and stuf out older attachments:
                    var minRevPos = FindCommonAncestor(populatedRev, backTo);
                    try {
                        LocalDatabase.ExpandAttachments(populatedRev, minRevPos + 1, !_dontSendMultipart, false);
                    } catch (Exception ex) {
                        Log.To.Sync.E(TAG, "Error expanding attachments, marking revision failed", ex);
                        RevisionFailed();
                        continue;
                    }

                    properties = populatedRev.GetProperties();
                    if (!_dontSendMultipart && UploadMultipartRevision(populatedRev))
                    {
                        continue;
                    }
                }

                if (properties == null || !properties.ContainsKey("_id"))
                {
                    throw Misc.CreateExceptionAndLog(Log.To.Sync, StatusCode.BadParam, TAG,
                                                     "properties must contain a document _id");
                }

                // Add the _revisions list:
                revsToSend.Add(rev);

                //now add it to the docs to send
                docsToSend.Add(properties);
            }

            UploadBulkDocs(docsToSend, revsToSend);
        }
        internal RevisionInternal PutDocument(string docId, IDictionary<string, object> properties, string prevRevId, bool allowConflict, Status resultStatus)
        {
            bool deleting = properties == null || properties.GetCast<bool>("_deleted");
            Log.D(TAG, "PUT _id={0}, _rev={1}, _deleted={2}, allowConflict={3}", docId, prevRevId, deleting, allowConflict);
            if ((prevRevId != null && docId == null) || (deleting && docId == null)) {
                if (resultStatus != null) {
                    resultStatus.Code = StatusCode.BadId;
                    return null;
                }
            }

            if (properties != null && properties.Get("_attachments").AsDictionary<string, object>() != null) {
                var tmpRev = new RevisionInternal(docId, prevRevId, deleting);
                tmpRev.SetProperties(properties);
                if (!ProcessAttachmentsForRevision(tmpRev, prevRevId, resultStatus)) {
                    return null;
                }

                properties = tmpRev.GetProperties();
            }

            StoreValidation validationBlock = null;
            if (Shared.HasValues("validation", Name)) {
                validationBlock = ValidateRevision;
            }

            var putRev = Storage.PutRevision(docId, prevRevId, properties, deleting, allowConflict, validationBlock, resultStatus);
            if (putRev != null) {
                Log.D(TAG, "--> created {0}", putRev);
                if (!string.IsNullOrEmpty(docId)) {
                    UnsavedRevisionDocumentCache.Remove(docId);
                }
            }

            return putRev;
        }
        internal Status Compile(IDictionary<string, object> viewProps, string language)
        {
            language = language ?? "javascript";
            string mapSource = viewProps.Get("map") as string;
            if (mapSource == null) {
                return new Status(StatusCode.NotFound);
            }

            MapDelegate mapDelegate = Compiler.CompileMap(mapSource, language);
            if (mapDelegate == null) {
                Log.W(TAG, "View {0} could not compile {1} map fn: {2}", Name, language, mapSource);
                return new Status(StatusCode.CallbackError);
            }

            string reduceSource = viewProps.Get("reduce") as string;
            ReduceDelegate reduceDelegate = null;
            if (reduceSource != null) {
                reduceDelegate = Compiler.CompileReduce(reduceSource, language);
                if (reduceDelegate == null) {
                    Log.W(TAG, "View {0} could not compile {1} reduce fn: {2}", Name, language, mapSource);
                    return new Status(StatusCode.CallbackError);
                }
            }
                
            string version = Misc.HexSHA1Digest(Manager.GetObjectMapper().WriteValueAsBytes(viewProps));
            SetMapReduce(mapDelegate, reduceDelegate, version);
            DocumentType = viewProps.GetCast<string>("documentType");

            var options = viewProps.Get("options").AsDictionary<string, object>();
            Collation = ViewCollation.Unicode;
            if (options != null && options.ContainsKey("collation")) {
                string collation = options["collation"] as string;
                if (collation.ToLower().Equals("raw")) {
                    Collation = ViewCollation.Raw;
                }
            }

            return new Status(StatusCode.Ok);
        }
        private static void ProcessBody(IDictionary<string, object> body)
        {
            var feed = body.GetCast<string>("feed");
            if (feed != null) {
                if (feed.Equals("longpoll")) {
                    body["feed"] = ChangesFeedMode.LongPoll;
                } else if (feed.Equals("continuous")) {
                    body["feed"] = ChangesFeedMode.Continuous;
                } else if (feed.Equals("eventsource")) {
                    body["feed"] = ChangesFeedMode.EventSource;
                }
            } else {
                body["feed"] = ChangesFeedMode.Normal;
            }

            var contentOptions = DocumentContentOptions.None;
            if (body.GetCast<bool>("attachments")) {
                contentOptions |= DocumentContentOptions.IncludeAttachments;
            }

            if (body.GetCast<bool>("local_seq")) {
                contentOptions |= DocumentContentOptions.IncludeLocalSeq;
            }

            if (body.GetCast<bool>("conflicts")) {
                contentOptions |= DocumentContentOptions.IncludeConflicts;
            }

            if (body.GetCast<bool>("revs")) {
                contentOptions |= DocumentContentOptions.IncludeRevs;
            }

            if (body.GetCast<bool>("revs_info")) {
                contentOptions |= DocumentContentOptions.IncludeRevsInfo;
            }

            body["content_options"] = contentOptions;
        }
        private bool ParseTokens(IDictionary<string, object> tokens)
        {
            var idToken = tokens.GetCast<string>("id_token");
            if(idToken == null) {
                return false;
            }

            IDToken = idToken;
            var newRefreshToken = tokens.GetCast<string>("refresh_token");
            if (newRefreshToken != null) {
                RefreshToken = newRefreshToken;
            }

            Username = tokens.GetCast<string>("name");
            _haveSessionCookie = tokens.ContainsKey("session_id");
            return true;
        }