Beispiel #1
0
        internal static bool ShowPrivateUserInfo(UserBE u)
        {
            DekiContext context = DekiContext.Current;

            //Requesting user is able to see private user details if looking up self, setting privacy/expose-user-email is true, or an apikey is provided, or admin
            return((context.Instance.PrivacyExposeUserEmail) || (context.User != null && context.User.ID == u.ID) || (context.Deki.DetermineAccess(DreamContext.Current, DreamContext.Current.Request) == DreamAccess.Internal));
        }
Beispiel #2
0
 public DreamMessage GetContentHandler(DreamContext context, DreamMessage message) {
     user user = Authenticate(context, message, DekiUserLevel.User);
     page page = Authorize(context, user, DekiAccessLevel.Read, "pageid");
     DekiContext deki = new DekiContext(message, this.DekiConfig);
     bool nofollow = (context.Uri.GetParam("nofollow", 0, null) != null);
     string contents = page.getContent(nofollow);
     string xml = string.Format(DekiWikiService.XHTML_LOOSE, contents);
     XDoc doc = XDoc.FromXml(xml);
     if (doc == null) {
         LogUtils.LogWarning(_log, "GetContentHandler: null document page content", page.PrefixedName, contents);
         throw new DreamAbortException(DreamMessage.BadRequest("null document"));
     }
     XDoc result = new XDoc("list");
     string type = context.Uri.GetParam("type", 0, null);
     string id = context.Uri.GetParam("id", 0, null);
     if (id != null) {
         XDoc widget = doc[string.Format("//default:span[@widgetid={0}]", id)];
         if (widget.IsEmpty) {
             LogUtils.LogWarning(_log, "GetContentHandler: widget not found for ID", id);
             return DreamMessage.NotFound("");
         }
         LogUtils.LogTrace(_log, "GetContentHandler: widget by id (id, xspan)", id, widget);
         result.Add(ConvertFromXSpan(widget));
     } else if (type != null) {
         foreach (XDoc widget in doc[string.Format("//default:span[@widgettype='{0}']", type)])
             result.Add(ConvertFromXSpan(widget));
         LogUtils.LogTrace(_log, "GetContentHandler: widget by type (type, #)", type, result.Count);
     } else {
         foreach (XDoc widget in doc["//default:span[@class='widget']"])
             result.Add(ConvertFromXSpan(widget));
         LogUtils.LogTrace(_log, "GetContentHandler: all widgets (#)", type, result.Count);
     }
     return DreamMessage.Ok(result);
 }
Beispiel #3
0
        internal void CheckResponseCache(DreamContext context, bool longTimeout)
        {
            DekiContext deki = DekiContext.Current;

            if (UserBL.IsAnonymous(deki.User) && deki.Instance.CacheAnonymousOutput)
            {
                string key = string.Format("{0}.{1}", deki.User.ID, context.Uri);
                CheckResponseCache(key);
                context.CacheKeyAndTimeout = new Tuplet <object, TimeSpan>(key, longTimeout ? deki.Instance.CacheAnonymousOutputLong : deki.Instance.CacheAnonymousOutputShort);
            }
        }
Beispiel #4
0
        public DreamMessage GetContentHandler(DreamContext context, DreamMessage message)
        {
            user        user     = Authenticate(context, message, DekiUserLevel.User);
            page        page     = Authorize(context, user, DekiAccessLevel.Read, "pageid");
            DekiContext deki     = new DekiContext(message, this.DekiConfig);
            bool        nofollow = (context.Uri.GetParam("nofollow", 0, null) != null);
            string      contents = page.getContent(nofollow);
            string      xml      = string.Format(DekiWikiService.XHTML_LOOSE, contents);
            XDoc        doc      = XDoc.FromXml(xml);

            if (doc == null)
            {
                LogUtils.LogWarning(_log, "GetContentHandler: null document page content", page.PrefixedName, contents);
                throw new DreamAbortException(DreamMessage.BadRequest("null document"));
            }
            XDoc   result = new XDoc("list");
            string type   = context.Uri.GetParam("type", 0, null);
            string id     = context.Uri.GetParam("id", 0, null);

            if (id != null)
            {
                XDoc widget = doc[string.Format("//default:span[@widgetid={0}]", id)];
                if (widget.IsEmpty)
                {
                    LogUtils.LogWarning(_log, "GetContentHandler: widget not found for ID", id);
                    return(DreamMessage.NotFound(""));
                }
                LogUtils.LogTrace(_log, "GetContentHandler: widget by id (id, xspan)", id, widget);
                result.Add(ConvertFromXSpan(widget));
            }
            else if (type != null)
            {
                foreach (XDoc widget in doc[string.Format("//default:span[@widgettype='{0}']", type)])
                {
                    result.Add(ConvertFromXSpan(widget));
                }
                LogUtils.LogTrace(_log, "GetContentHandler: widget by type (type, #)", type, result.Count);
            }
            else
            {
                foreach (XDoc widget in doc["//default:span[@class='widget']"])
                {
                    result.Add(ConvertFromXSpan(widget));
                }
                LogUtils.LogTrace(_log, "GetContentHandler: all widgets (#)", type, result.Count);
            }
            return(DreamMessage.Ok(result));
        }
Beispiel #5
0
        internal static string AsPublicUiUri(Title title, bool forceUseIndexPhp)
        {
            DreamContext context = DreamContext.Current;
            string       baseUri = context.GetParam("baseuri", null);

            if (baseUri != null)
            {
                return(baseUri + title.AsUiUriPath(true).Substring(Title.INDEX_PHP_TITLE.Length));
            }
            else
            {
                DekiContext deki   = DekiContext.Current;
                XUri        global = context.AsPublicUri(deki.UiUri.Uri.WithoutPathQueryFragment().WithoutCredentials());
                return(global.SchemeHostPort + "/" + title.AsUiUriPath(forceUseIndexPhp));
            }
        }
Beispiel #6
0
        public DreamMessage GetNavHandler(DreamContext context, DreamMessage message)
        {
            DekiContext deki = null; // new DekiContext(message, this.DekiConfig);
            page        cur  = null; // deki.GetCur(true); Max: Commented out to allow compilation

            if (cur == null)
            {
                return(DreamMessage.BadRequest("can't load page"));
            }
            int  maxLevel                = context.Uri.GetParam <int>("max-level", 1);
            bool filterRedirects         = context.Uri.GetParam <bool>("redirects", true);
            ICollection <string> columns = context.Uri.GetParams("column");
            string filter                = filterRedirects ? " AND page_is_redirect=0" : "";

            XDoc ret = new XDoc("nav");

            AddCur(deki, cur, ret, columns, filter, maxLevel);
            return(DreamMessage.Ok(ret));
        }
 void AddCur(DekiContext deki, page cur, XDoc doc, ICollection<string> columns, string filter, int level, bool flat, bool addWrap) {
     if (level < 0)
         return;
     if (addWrap)
         doc.Start("page");
     doc.Attr("cur-id", cur.ID.ToString());
     if (columns.Count == 0 || columns.Contains("parent"))
         doc.Start("parent").Value(cur.ParentID).End();
     if (columns.Count == 0 || columns.Contains("name"))
         doc.Start("name").Value(cur.PrefixedName).End();
     if (columns.Count == 0 || columns.Contains("modified"))
         doc.Start("modified").Value(cur.TimeStamp).End();
     if (columns.Count == 0 || columns.Contains("comment"))
         doc.Start("comment").Value(cur.Comment).End();
     if (columns.Count == 0 || columns.Contains("preview"))
         doc.Start("preview").Value(cur.TIP).End();
     if (columns.Count == 0 || columns.Contains("table-of-contents"))
         doc.Start("table-of-contents").Value(cur.TOC).End();
     if (columns.Count == 0 || columns.Contains("is-redirect"))
         doc.Start("is-redirect").Value(cur.IsRedirect.ToString()).End();
     if (columns.Count == 0 || columns.Contains("from-links"))
         AddCurIDList(cur.GetLinkIDsFrom(), "from-links", doc);
     if (columns.Count == 0 || columns.Contains("to-links"))
         AddCurIDList(cur.GetLinkIDsTo(), "to-links", doc);
     if (columns.Count == 0 || columns.Contains("files")) {
         doc.Start("files");
         foreach (attachments attachment in cur.GetAttachments())
             AddFile(attachment, doc);
         doc.End();
     }
     if (level < 1) {
         if (addWrap)
             doc.End();
         return;
     }
     if (columns.Count == 0 || columns.Contains("children")) {
         if (flat)
             AddCurIDList(cur.GetChildIDs(filter), "children", doc);
         else {
             doc.Start("children");
             foreach (page child in cur.LoadChildren(filter))
                 AddCur(deki, child, doc, columns, filter, level - 1);
             doc.End();
         }
     }
     if (addWrap)
         doc.End();
 }
Beispiel #8
0
        protected Yield PrologueDekiContext(DreamContext context, DreamMessage request, Result <DreamMessage> response)
        {
            // check if we need to skip this feature
            if (context.Feature.PathSegments.Length > 1 && context.Feature.PathSegments[context.Feature.ServiceUri.Segments.Length].StartsWith("@"))
            {
                response.Return(request);
                yield break;
            }
            if (context.Feature.Signature.StartsWithInvariantIgnoreCase("host") && !context.Feature.Signature.EqualsInvariantIgnoreCase("host/stop"))
            {
                // all host features except host/stop drop out before dekicontext so that there is no instance data
                response.Return(request);
                yield break;
            }
            var startInstanceIfNotRunning = true;

            if (context.Feature.Signature.StartsWithInvariantIgnoreCase("host/"))
            {
                startInstanceIfNotRunning = false;
            }

            // check if service has initialized
            if (!_isInitialized)
            {
                throw new DreamInternalErrorException("service not initialized");
            }

            //Build the dekicontext out of current request details and info from this wiki instance's details
            DekiInstance instance = _instanceManager.GetWikiInstance(request, startInstanceIfNotRunning);

            if (instance == null && !startInstanceIfNotRunning)
            {
                _log.Debug("no instance found, and on a code path that functions without one");
                response.Return(request);
                yield break;
            }

            // TODO (arnec): need to be able to get DreamContext.Current.StartTime injected into a feature signature
            var hostheader  = request.Headers.Host ?? string.Empty;
            var dekiContext = new DekiContext(this, instance, hostheader, context.StartTime, ResourceManager);

            // Note (arnec): By attaching DekiContext to the current DreamContext we guarantee that it is disposed at the end of the request
            DreamContext.Current.SetState(dekiContext);

            // check if instance has already been initialized
            if (instance != null)
            {
                if (instance.Status == DekiInstanceStatus.CREATED)
                {
                    bool created;
                    try {
                        lock (instance) {
                            created = (instance.Status == DekiInstanceStatus.CREATED);
                            if (created)
                            {
                                // initialize instance
                                instance.Startup(dekiContext);
                            }
                        }

                        // BUGBUGBUG (steveb): we startup the services AFTER the lock, because of race conditions, but this needs to be fixed
                        if (created)
                        {
                            instance.StartServices();
                        }
                    } catch (Exception e) {
                        created = false;
                        instance.StatusDescription = "Initialization exception: " + e.GetCoroutineStackTrace();
                        instance.Log.Error("Error initializing instance", e);
                    }
                    if (created)
                    {
                        // Note (arnec) this has to happen down here, since yield cannot exist inside a try/catch
                        // send instance settings to mailer
                        yield return(Coroutine.Invoke(ConfigureMailer, ConfigBL.GetInstanceSettingsAsDoc(false), new Result()).CatchAndLog(_log));

                        // check whether we have an index
                        XDoc lucenestate = null;
                        yield return(LuceneIndex.At("initstate").With("wikiid", instance.Id).Get(new Result <XDoc>()).Set(x => lucenestate = x));

                        // Note (arnec): defaulting to true, to avoid accidental re-index on false positive
                        if (!(lucenestate["@exists"].AsBool ?? true))
                        {
                            _log.DebugFormat("instance '{0}' doesn't have an index yet, forcing a rebuild", instance.Id);
                            yield return(Self.At("site", "search", "rebuild").With("apikey", MasterApiKey).Post(new Result <DreamMessage>()));
                        }
                    }
                }
                if (instance.Status != DekiInstanceStatus.ABANDONED)
                {
                    try {
                        // force a state check to verify that license is good
                        var state = dekiContext.LicenseManager.LicenseState;
                        _log.DebugFormat("instance '{0}' license state: {1}", instance.Id, state);
                    } catch (MindTouchRemoteLicenseFailedException) {
                        _instanceManager.ShutdownCurrentInstance();
                    }
                }
                instance.CheckInstanceIsReady();
                if (instance.Status == DekiInstanceStatus.ABANDONED)
                {
                    //If instance was abandoned (failed to initialize), error out.
                    throw new DreamInternalErrorException(string.Format("wiki '{0}' has failed to initialize or did not start up properly: {1}", instance.Id, instance.StatusDescription));
                }
                if (instance.Status == DekiInstanceStatus.STOPPING)
                {
                    throw new DreamInternalErrorException(string.Format("wiki '{0}' is currently shutting down", instance.Id));
                }
                if (instance.Status == DekiInstanceStatus.STOPPED)
                {
                    throw new DreamInternalErrorException(string.Format("wiki '{0}' has just shut down and may be restarted with a new request", instance.Id));
                }

                // intialize culture/language + user
                if (context.Culture.IsNeutralCulture || context.Culture.Equals(System.Globalization.CultureInfo.InvariantCulture))
                {
                    try {
                        context.Culture = new System.Globalization.CultureInfo(instance.SiteLanguage);
                    } catch {
                        // in case the site language is invalid, default to US English
                        context.Culture = new System.Globalization.CultureInfo("en-US");
                    }
                }
                if (!context.Feature.Signature.EqualsInvariantIgnoreCase("users/authenticate"))
                {
                    bool allowAnon = context.Uri.GetParam("authenticate", "false").EqualsInvariantIgnoreCase("false");
                    bool altPassword;
                    SetContextAndAuthenticate(request, 0, false, allowAnon, false, out altPassword);
                }

                // TODO (steveb): we should update the culture based on the user's preferences
            }

            // continue processing
            response.Return(request);
            yield break;
        }
Beispiel #9
0
 void AddCur(DekiContext deki, page cur, XDoc doc, ICollection <string> columns, string filter, int level, bool flat, bool addWrap)
 {
     if (level < 0)
     {
         return;
     }
     if (addWrap)
     {
         doc.Start("page");
     }
     doc.Attr("cur-id", cur.ID.ToString());
     if (columns.Count == 0 || columns.Contains("parent"))
     {
         doc.Start("parent").Value(cur.ParentID).End();
     }
     if (columns.Count == 0 || columns.Contains("name"))
     {
         doc.Start("name").Value(cur.PrefixedName).End();
     }
     if (columns.Count == 0 || columns.Contains("modified"))
     {
         doc.Start("modified").Value(cur.TimeStamp).End();
     }
     if (columns.Count == 0 || columns.Contains("comment"))
     {
         doc.Start("comment").Value(cur.Comment).End();
     }
     if (columns.Count == 0 || columns.Contains("preview"))
     {
         doc.Start("preview").Value(cur.TIP).End();
     }
     if (columns.Count == 0 || columns.Contains("table-of-contents"))
     {
         doc.Start("table-of-contents").Value(cur.TOC).End();
     }
     if (columns.Count == 0 || columns.Contains("is-redirect"))
     {
         doc.Start("is-redirect").Value(cur.IsRedirect.ToString()).End();
     }
     if (columns.Count == 0 || columns.Contains("from-links"))
     {
         AddCurIDList(cur.GetLinkIDsFrom(), "from-links", doc);
     }
     if (columns.Count == 0 || columns.Contains("to-links"))
     {
         AddCurIDList(cur.GetLinkIDsTo(), "to-links", doc);
     }
     if (columns.Count == 0 || columns.Contains("files"))
     {
         doc.Start("files");
         foreach (attachments attachment in cur.GetAttachments())
         {
             AddFile(attachment, doc);
         }
         doc.End();
     }
     if (level < 1)
     {
         if (addWrap)
         {
             doc.End();
         }
         return;
     }
     if (columns.Count == 0 || columns.Contains("children"))
     {
         if (flat)
         {
             AddCurIDList(cur.GetChildIDs(filter), "children", doc);
         }
         else
         {
             doc.Start("children");
             foreach (page child in cur.LoadChildren(filter))
             {
                 AddCur(deki, child, doc, columns, filter, level - 1);
             }
             doc.End();
         }
     }
     if (addWrap)
     {
         doc.End();
     }
 }
Beispiel #10
0
        private bool ProcessExternalLink(XDoc addr, ParserMode mode, DekiContext deki) {

            // first check whether this link has already been marked as internal
            if(addr["@class"].Contents.ContainsInvariant("internal")) {
                return false;
            }
            bool isExternal = false;
            string link = addr["@href"].Contents.Trim();
            XUri xuri = XUri.TryParse(link);

            // check if the link is absolute, but points back into the wiki
            if((xuri != null) && xuri.HasPrefix(deki.UiUri) && !xuri.HasPrefix(deki.ApiUri) && (xuri.UserInfo == null)) {
                addr["@href"].ReplaceValue(xuri.PathQueryFragment);
                return false;
            }

            // try to parse the link as a uri
            string classAttribute = null;
            if(xuri != null) {
                isExternal = true;
                switch(xuri.Scheme.ToLowerInvariant()) {
                case "https":
                    classAttribute = "link-https";
                    break;
                case "news":
                    classAttribute = "link-news";
                    break;
                case "mailto":
                    classAttribute = "link-mailto";
                    break;
                case "ftp":
                    classAttribute = "link-ftp";
                    break;
                case "irc":
                    classAttribute = "link-irc";
                    break;
                default:
                    classAttribute = "external";
                    break;
                }
            } else {

                // check if the link is an email address 
                Match match = MAILTO_REGEX.Match(link);
                if(match.Success) {
                    int index = match.Value.IndexOf('@');
                    if((0 < index) && (-1 < match.Value.IndexOf('.', index))) {
                        isExternal = true;
                        if(!match.Value.StartsWithInvariantIgnoreCase("mailto:")) {
                            addr["@href"].ReplaceValue("mailto:" + match.Value);
                        }
                        classAttribute = "link-mailto";
                    }
                }
            }

            // if the link is external, set its attributes accordingly
            if(isExternal) {
                addr.RemoveAttr("fileid");
                if(ParserMode.EDIT == mode) {
                    classAttribute = "external";
                } else if(ParserMode.VIEW == mode || ParserMode.VIEW_NO_EXECUTE == mode) {
                    if(deki.Instance.WebLinkNoFollow) {
                        addr.Attr("rel", "external nofollow");
                    } else {
                        addr.Attr("rel", "external");
                    }
                    addr.Attr("href", link);
                    if(addr["@title"].IsEmpty) {
                        addr.Attr("title", link);
                    }
                    string target = (deki.Instance.ExternalLinkTarget ?? "_blank").Trim();
                    if(addr["@target"].IsEmpty && (target.Length > 0)) {
                        addr.Attr("target", target);
                    }
                }

                // remove known class attribute and add new class attribute if needed
                List<string> attributes = new List<string>(addr["@class"].Contents.Split(' '));
                attributes.Remove("link-https");
                attributes.Remove("link-news");
                attributes.Remove("link-mailto");
                attributes.Remove("link-ftp");
                attributes.Remove("link-irc");
                attributes.Remove("external");
                if(!attributes.Contains(classAttribute)) {
                    attributes.Add(classAttribute);
                }
                XDoc cls = addr["@class"];
                if(cls.IsEmpty) {
                    addr.Attr("class", string.Join(" ", attributes.ToArray()));
                } else {
                    cls.ReplaceValue(string.Join(" ", attributes.ToArray()));
                }
            }
            return isExternal;
        }
Beispiel #11
0
        internal bool TryGetServiceLicense(XUri sid, out string license, out DateTime?expiration)
        {
            license    = null;
            expiration = null;

            // NOTE: context may be null since services are created by DekiWikiService.Start as well as by ServiceBL.
            // This method only applies for extension services which are started with a context.
            DekiContext context = DekiContext.CurrentOrNull;

            if ((sid != null) && (context != null) && (context.LicenseManager.LicenseDocument != null))
            {
                foreach (XDoc service in context.LicenseManager.LicenseDocument["grants/service-license"])
                {
                    string text = service.AsText;

                    // check if the licensed SID matches the requested SID
                    XUri licensedSID = service["@sid"].AsUri;
                    if (licensedSID == null)
                    {
                        // parse service-license contents for the SID
                        Dictionary <string, string> values = HttpUtil.ParseNameValuePairs(text);

                        // check if the licensed SID matches the requested SID
                        string licensedSIDText;
                        if (values.TryGetValue("sid", out licensedSIDText) && XUri.TryParse(licensedSIDText, out licensedSID) && sid.HasPrefix(licensedSID, true))
                        {
                            // check if the licensed SID has an expiration date
                            string   licenseExpireText;
                            DateTime licenseExpire;
                            if (values.TryGetValue("expire", out licenseExpireText) && DateTime.TryParse(licenseExpireText, out licenseExpire))
                            {
                                if (licenseExpire >= DateTime.UtcNow)
                                {
                                    license    = text;
                                    expiration = licenseExpire;
                                    return(true);
                                }
                            }
                            else
                            {
                                license = text;
                                return(true);
                            }
                        }
                    }
                    else if (sid.HasPrefix(licensedSID, true))
                    {
                        DateTime?expire = service["@date.expire"].AsDate;

                        // check if the licensed SID has an expiration date
                        if ((expire == null) || (expire >= DateTime.UtcNow))
                        {
                            license    = text;
                            expiration = expire;
                            return(true);
                        }
                    }
                }
            }
            return(false);
        }
        /// <summary>
        /// Calls methods to perform initial init of a wiki instance including db updates, etc
        /// </summary>
        public void Startup(DekiContext context) {
            if(context == null) {
                throw new ArgumentNullException("context");
            }
            if(_status != DekiInstanceStatus.CREATED) {
                throw new InvalidOperationException("bad state");
            }
            _status = DekiInstanceStatus.INITIALIZING;

            // run startup code
            try {

                // create the IDekiDataSessionFactory for this instance
                Type typeMySql = Type.GetType("MindTouch.Deki.Data.MySql.MySqlDekiDataSessionFactory, mindtouch.deki.data.mysql", true);
                Type typeCaching = null;
                try {
                    typeCaching = Type.GetType("MindTouch.Deki.Data.Caching.CachingDekiDataSessionFactory, mindtouch.data.caching", false);
                } catch(Exception x) {
                    Log.Warn("The caching library was found but could not be loaded. Check that its version matches the version of your MindTouch API", x);
                }

                IDekiDataSessionFactory factoryMySql = (IDekiDataSessionFactory)typeMySql.GetConstructor(Type.EmptyTypes).Invoke(null);
                IDekiDataSessionFactory factoryLogging = new LoggingDekiDataSessionFactory(factoryMySql);

                if(typeCaching != null) {
                    IDekiDataSessionFactory factoryCaching = (IDekiDataSessionFactory)typeCaching.GetConstructor(new[] { typeof(IDekiDataSessionFactory) }).Invoke(new object[] { factoryLogging });
                    _sessionFactory = factoryCaching;
                } else {
                    _sessionFactory = factoryLogging;
                }

                _sessionFactory.Initialize(Config ?? _deki.Config, new DekiInstanceSettings());

                try {
                    DbUtils.CurrentSession = _sessionFactory.CreateSession();

                    // check for 'api-key'
                    if(string.IsNullOrEmpty(ApiKey) && string.IsNullOrEmpty(_deki.MasterApiKey)) {
                        throw new ArgumentNullException("apikey", "Missing apikey for wiki instance. Please ensure that you have a global <apikey> defined (in the service settings xml file) or an instance specific key in the config table as 'security/api-key'.");
                    }

                    // check if a storage config section was provided (default storage is filesystem provider)
                    XDoc storageConfig;
                    switch(ConfigBL.GetInstanceSettingsValue("storage/@type", ConfigBL.GetInstanceSettingsValue("storage/type", "default"))) {
                    case "default":
                        string defaultAttachPath = Path.Combine(_deki.DekiPath, "attachments");
                        storageConfig = new XDoc("config")
                            .Elem("path", defaultAttachPath)
                            .Elem("cache-path", Path.Combine(defaultAttachPath, ".cache"));
                        _storage = new FSStorage(storageConfig);
                        break;
                    case "fs":
                        string fsPath = ConfigBL.GetInstanceSettingsValue("storage/fs/path", null);

                        //Replace a $1 with the wiki name                
                        fsPath = string.Format(Dream.PhpUtil.ConvertToFormatString(fsPath ?? string.Empty), Id);
                        storageConfig = new XDoc("config")
                            .Elem("path", fsPath)
                            .Elem("cache-path", ConfigBL.GetInstanceSettingsValue("storage/fs/cache-path", null));
                        _storage = new FSStorage(storageConfig);
                        break;
                    case "s3":
                        storageConfig = new XDoc("config")
                            .Elem("publickey", ConfigBL.GetInstanceSettingsValue("storage/s3/publickey", null))
                            .Elem("privatekey", ConfigBL.GetInstanceSettingsValue("storage/s3/privatekey", null))
                            .Elem("bucket", ConfigBL.GetInstanceSettingsValue("storage/s3/bucket", null))
                            .Elem("prefix", string.Format(Dream.PhpUtil.ConvertToFormatString(ConfigBL.GetInstanceSettingsValue("storage/s3/prefix", string.Empty)), DekiContext.Current.Instance.Id))
                            .Elem("timeout", ConfigBL.GetInstanceSettingsValue("storage/s3/timeout", null))
                            .Elem("allowredirects", ConfigBL.GetInstanceSettingsValue("storage/s3/allowredirects", null))
                            .Elem("redirecttimeout", ConfigBL.GetInstanceSettingsValue("storage/s3/redirecttimeout", null));
                        _storage = new S3Storage(storageConfig);
                        break;
                    default:
                        throw new ArgumentException("Storage provider unknown or not defined (key: storage/type)", "storage/type");
                    }

                    HomePageId = DbUtils.CurrentSession.Pages_HomePageId;
                } finally {
                    if(null != DbUtils.CurrentSession) {
                        DbUtils.CurrentSession.Dispose();
                        DbUtils.CurrentSession = null;
                    }
                }
                _eventSink = new DekiChangeSink(Id, DekiContext.Current.ApiUri, DekiContext.Current.Deki.PubSub.At("publish").WithCookieJar(DekiContext.Current.Deki.Cookies));
                _eventSink.InstanceStarting(DreamContext.Current.StartTime);
            } catch {

                // we failed to initialize
                _status = DekiInstanceStatus.ABANDONED;
                throw;
            }

            // set state to initializing
            _status = DekiInstanceStatus.INITIALIZING;
        }
Beispiel #13
0
        /// <summary>
        /// Calls methods to perform initial init of a wiki instance including db updates, etc
        /// </summary>
        public void Startup(DekiContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if (_status != DekiInstanceStatus.CREATED)
            {
                throw new InvalidOperationException("bad state");
            }
            _status = DekiInstanceStatus.INITIALIZING;

            // run startup code
            try {
                // have to initialize the event sink first since because license checking might trigger a license transition event
                _eventSink = new DekiChangeSink(Id, DekiContext.Current.ApiUri, DekiContext.Current.Deki.PubSub.At("publish").WithCookieJar(DekiContext.Current.Deki.Cookies));

                // create the IDekiDataSessionFactory for this instance
                Type typeMySql   = Type.GetType("MindTouch.Deki.Data.MySql.MySqlDekiDataSessionFactory, mindtouch.deki.data.mysql", true);
                Type typeCaching = null;
                try {
                    typeCaching = Type.GetType("MindTouch.Deki.Data.Caching.CachingDekiDataSessionFactory, mindtouch.data.caching", false);
                } catch (Exception x) {
                    Log.Warn("The caching library was found but could not be loaded. Check that its version matches the version of your MindTouch API", x);
                }

                // FUN FACT: DekiInstanceSettings is a proxy for the static calls in ConfigBL, which expects to access DbUtils.CurrentSession,
                // which has to be initialized by the session factory.. Yes, the very session factory that takes DekiInstanceSettings as part
                // of its initialization call. I call it the M.C.Escher Design Pattern.
                var sessionFactory = (IDekiDataSessionFactory)typeMySql.GetConstructor(Type.EmptyTypes).Invoke(null);
                sessionFactory = new LoggingDekiDataSessionFactory(sessionFactory);
                sessionFactory = new DekiDataSessionProfilerFactory(sessionFactory);
                if (typeCaching != null)
                {
                    sessionFactory = (IDekiDataSessionFactory)typeCaching.GetConstructor(new[] { typeof(IDekiDataSessionFactory) }).Invoke(new object[] { sessionFactory });
                }
                _sessionFactory = sessionFactory;
                _sessionFactory.Initialize(Config ?? _deki.Config, _licenseController.LicenseDoc, new DekiInstanceSettings());
                var state = context.LicenseManager.LicenseState;
                if (state == LicenseStateType.UNDEFINED)
                {
                    throw new MindTouchInvalidLicenseStateException();
                }

                // set deki license token
                _token = context.LicenseManager.LicenseDocument["licensee/deki"].AsText;
                if (string.IsNullOrEmpty(_token))
                {
                    // compute deki license token
                    var    tokenKey = _apiKey ?? _deki.MasterApiKey;
                    ulong  folded_productkey_md5 = 0;
                    byte[] md5_bytes             = StringUtil.ComputeHash(tokenKey, Encoding.UTF8);
                    for (int i = 0; i < md5_bytes.Length; ++i)
                    {
                        folded_productkey_md5 ^= (ulong)md5_bytes[i] << (i & 7) * 8;
                    }
                    _token = folded_productkey_md5.ToString("X");
                }

                // check if a storage config section was provided (default storage is filesystem provider)
                XDoc storageConfig;
                switch (ConfigBL.GetInstanceSettingsValue("storage/@type", ConfigBL.GetInstanceSettingsValue("storage/type", "default")))
                {
                case "default":
                    string defaultAttachPath = Path.Combine(_deki.DekiPath, "attachments");
                    storageConfig = new XDoc("config")
                                    .Elem("path", defaultAttachPath)
                                    .Elem("cache-path", Path.Combine(defaultAttachPath, ".cache"));
                    _storage = new FSStorage(storageConfig);
                    break;

                case "fs":
                    string fsPath = ConfigBL.GetInstanceSettingsValue("storage/fs/path", null);

                    //Replace a $1 with the wiki name
                    fsPath        = string.Format(PhpUtil.ConvertToFormatString(fsPath ?? string.Empty), Id);
                    storageConfig = new XDoc("config")
                                    .Elem("path", fsPath)
                                    .Elem("cache-path", ConfigBL.GetInstanceSettingsValue("storage/fs/cache-path", null));
                    _storage = new FSStorage(storageConfig);
                    break;

                case "s3":
                    storageConfig = new XDoc("config")
                                    .Elem("publickey", ConfigBL.GetInstanceSettingsValue("storage/s3/publickey", null))
                                    .Elem("privatekey", ConfigBL.GetInstanceSettingsValue("storage/s3/privatekey", null))
                                    .Elem("bucket", ConfigBL.GetInstanceSettingsValue("storage/s3/bucket", null))
                                    .Elem("prefix", string.Format(PhpUtil.ConvertToFormatString(ConfigBL.GetInstanceSettingsValue("storage/s3/prefix", string.Empty)), DekiContext.Current.Instance.Id))
                                    .Elem("timeout", ConfigBL.GetInstanceSettingsValue("storage/s3/timeout", null))
                                    .Elem("allowredirects", ConfigBL.GetInstanceSettingsValue("storage/s3/allowredirects", null))
                                    .Elem("redirecttimeout", ConfigBL.GetInstanceSettingsValue("storage/s3/redirecttimeout", null));
                    _storage = new S3Storage(storageConfig, _loggerRepository.Get <S3Storage>());
                    break;

                default:
                    throw new ArgumentException("Storage provider unknown or not defined (key: storage/type)", "storage/type");
                }

                HomePageId = DbUtils.CurrentSession.Pages_HomePageId;
                _eventSink.InstanceStarting(DekiContext.Current.Now);
            } catch {
                // we failed to initialize
                _status = DekiInstanceStatus.ABANDONED;
                throw;
            }

            // set state to initializing
            _status = DekiInstanceStatus.INITIALIZING;
        }
Beispiel #14
0
        /// <summary>
        /// Calls methods to perform initial init of a wiki instance including db updates, etc
        /// </summary>
        public void Startup(DekiContext context) {
            if(context == null) {
                throw new ArgumentNullException("context");
            }
            if(_status != DekiInstanceStatus.CREATED) {
                throw new InvalidOperationException("bad state");
            }
            _status = DekiInstanceStatus.INITIALIZING;

            // run startup code
            try {

                // have to initialize the event sink first since because license checking might trigger a license transition event
                _eventSink = new DekiChangeSink(Id, DekiContext.Current.ApiUri, DekiContext.Current.Deki.PubSub.At("publish").WithCookieJar(DekiContext.Current.Deki.Cookies));

                // create the IDekiDataSessionFactory for this instance
                Type typeMySql = Type.GetType("MindTouch.Deki.Data.MySql.MySqlDekiDataSessionFactory, mindtouch.deki.data.mysql", true);
                Type typeCaching = null;
                try {
                    typeCaching = Type.GetType("MindTouch.Deki.Data.Caching.CachingDekiDataSessionFactory, mindtouch.data.caching", false);
                } catch(Exception x) {
                    Log.Warn("The caching library was found but could not be loaded. Check that its version matches the version of your MindTouch API", x);
                }

                // FUN FACT: DekiInstanceSettings is a proxy for the static calls in ConfigBL, which expects to access DbUtils.CurrentSession,
                // which has to be initialized by the session factory.. Yes, the very session factory that takes DekiInstanceSettings as part
                // of its initialization call. I call it the M.C.Escher Design Pattern.
                var sessionFactory = (IDekiDataSessionFactory)typeMySql.GetConstructor(Type.EmptyTypes).Invoke(null);
                sessionFactory = new LoggingDekiDataSessionFactory(sessionFactory);
                sessionFactory = new DekiDataSessionProfilerFactory(sessionFactory);
                if(typeCaching != null) {
                    sessionFactory = (IDekiDataSessionFactory)typeCaching.GetConstructor(new[] { typeof(IDekiDataSessionFactory) }).Invoke(new object[] { sessionFactory });
                }
                _sessionFactory = sessionFactory;
                _sessionFactory.Initialize(Config ?? _deki.Config, _licenseController.LicenseDoc, new DekiInstanceSettings());
                var state = context.LicenseManager.LicenseState;
                if(state == LicenseStateType.UNDEFINED) {
                    throw new MindTouchInvalidLicenseStateException();
                }

                // set deki license token
                _token = context.LicenseManager.LicenseDocument["licensee/deki"].AsText;
                if(string.IsNullOrEmpty(_token)) {

                    // compute deki license token
                    var tokenKey = _apiKey ?? _deki.MasterApiKey;
                    ulong folded_productkey_md5 = 0;
                    byte[] md5_bytes = StringUtil.ComputeHash(tokenKey, Encoding.UTF8);
                    for(int i = 0; i < md5_bytes.Length; ++i) {
                        folded_productkey_md5 ^= (ulong)md5_bytes[i] << (i & 7) * 8;
                    }
                    _token = folded_productkey_md5.ToString("X");
                }

                // check if a storage config section was provided (default storage is filesystem provider)
                XDoc storageConfig;
                switch(ConfigBL.GetInstanceSettingsValue("storage/@type", ConfigBL.GetInstanceSettingsValue("storage/type", "default"))) {
                case "default":
                    string defaultAttachPath = Path.Combine(_deki.DekiPath, "attachments");
                    storageConfig = new XDoc("config")
                        .Elem("path", defaultAttachPath)
                        .Elem("cache-path", Path.Combine(defaultAttachPath, ".cache"));
                    _storage = new FSStorage(storageConfig);
                    break;
                case "fs":
                    string fsPath = ConfigBL.GetInstanceSettingsValue("storage/fs/path", null);

                    //Replace a $1 with the wiki name                
                    fsPath = string.Format(PhpUtil.ConvertToFormatString(fsPath ?? string.Empty), Id);
                    storageConfig = new XDoc("config")
                        .Elem("path", fsPath)
                        .Elem("cache-path", ConfigBL.GetInstanceSettingsValue("storage/fs/cache-path", null));
                    _storage = new FSStorage(storageConfig);
                    break;
                case "s3":
                    storageConfig = new XDoc("config")
                        .Elem("publickey", ConfigBL.GetInstanceSettingsValue("storage/s3/publickey", null))
                        .Elem("privatekey", ConfigBL.GetInstanceSettingsValue("storage/s3/privatekey", null))
                        .Elem("bucket", ConfigBL.GetInstanceSettingsValue("storage/s3/bucket", null))
                        .Elem("prefix", string.Format(PhpUtil.ConvertToFormatString(ConfigBL.GetInstanceSettingsValue("storage/s3/prefix", string.Empty)), DekiContext.Current.Instance.Id))
                        .Elem("timeout", ConfigBL.GetInstanceSettingsValue("storage/s3/timeout", null))
                        .Elem("allowredirects", ConfigBL.GetInstanceSettingsValue("storage/s3/allowredirects", null))
                        .Elem("redirecttimeout", ConfigBL.GetInstanceSettingsValue("storage/s3/redirecttimeout", null));
                    _storage = new S3Storage(storageConfig, _loggerRepository.Get<S3Storage>());
                    break;
                default:
                    throw new ArgumentException("Storage provider unknown or not defined (key: storage/type)", "storage/type");
                }

                HomePageId = DbUtils.CurrentSession.Pages_HomePageId;
                _eventSink.InstanceStarting(DekiContext.Current.Now);
            } catch {

                // we failed to initialize
                _status = DekiInstanceStatus.ABANDONED;
                throw;
            }

            // set state to initializing
            _status = DekiInstanceStatus.INITIALIZING;
        }
Beispiel #15
0
 void AddCur(DekiContext deki, page cur, XDoc doc, ICollection <string> columns, string filter, int level)
 {
     AddCur(deki, cur, doc, columns, filter, level, false, true);
 }
 void AddCur(DekiContext deki, page cur, XDoc doc, ICollection<string> columns, string filter, int level) {
     AddCur(deki, cur, doc, columns, filter, level, false, true);
 }
Beispiel #17
0
        private void ProcessInternalLink(Regex fileRegex, XDoc addr, Title baseTitle, IList<Title> linksKnown, IList<string> linksBroken, List<Title> linksFound, List<Title> templates, DekiContext deki, out Title lookupLink) {
            lookupLink = null;
            XmlElement current = (XmlElement)addr.AsXmlNode;
            string template = current.GetAttribute("template");
            bool isTemplate = !String.IsNullOrEmpty(template);

            // if this is a link to an anchor on the current page, stop processing
            string href = current.GetAttribute("href").Trim();
            if((href.StartsWithInvariant("#") && !isTemplate) || (href.StartsWithInvariant("{{") && href.EndsWithInvariant("}}"))) {
                return;
            }

            // if this is a link to a subpage from an imported template, process it
            if((ParserMode.SAVE == _mode) && isTemplate) {
                addr.RemoveAttr("template");
                addr.RemoveAttr("class");
                Title templateTitle = Title.FromUIUri(baseTitle, template, false);
                templates.Add(templateTitle);

                // update the link to be relative to the current page instead of the template
                string[] segments = templateTitle.AsUnprefixedDbSegments();
                segments[0] = ".";
                template = String.Join("/", segments);
                addr.Attr("href", template);
                href = template;
            }
            List<string> css = new List<string>();
            bool containsImage = !addr[".//img"].IsEmpty;
            uint fileid;
            Title title;

            // Extract the fileid and title information
            // If the link is of the form "@api/deki/files/{fileid}/={filename}, extract the fileid and filename from it.
            // Otherwise, use the full URL as the title.
            Match m = fileRegex.Match(XUri.Decode(href));
            if(m.Success) {
                UInt32.TryParse(m.Groups["fileid"].Value, out fileid);
                title = Title.FromUIUri(null, "File:/" + m.Groups["filename"].Value);
            } else {
                uint.TryParse(current.GetAttribute("fileid"), out fileid);
                title = Title.FromUIUri(baseTitle, href);
            }

            // If the link has no text provide a default value
            if(addr.Contents.Length == 0) {
                addr.Value(title.IsFile ? title.Filename : Title.FromUIUri(null, href).AsUserFriendlyName());
            }

            // add the db encoded title to the list of links to lookup
            if(!title.IsFile && title.IsEditable) {
                linksFound.Add(title);
            }
            switch(_mode) {
            case ParserMode.EDIT:

                // if the title is a file, return a link in the form @api/deki/files/{fileid}/={filename}
                // TODO (brigettek):  Append the query param
                if(title.IsFile) {
                    if(null != _relToTitle) {

                        // Normalize file link if needed (for import/export)
                        Title fileTitle = GetTitleFromFileId(fileid);
                        if(null != fileTitle) {
                            addr.RemoveAttr("href");
                            addr.Attr("href.path", fileTitle.AsRelativePath(_relToTitle));
                            addr.Attr("href.filename", fileTitle.Filename);
                        }
                    } else {
                        if(0 == fileid) {
                            addr.Attr("href", title.AsEditorUriPath());
                        } else {
                            addr.Attr("href", deki.ApiUri.At("files", fileid.ToString(), Title.AsApiParam(title.Filename)).ToString());
                        }
                    }
                } else {

                    // TODO (brigettek):  Prevent resolving redirects for pages with many links to improve performance
                    // TODO (brigettek):  potential vulnerability.  The title is resolved without verifying that the user has browse permission to it. 
                    PageBE redirectedPage = PageBL.ResolveRedirects(PageBL.GetPageByTitle(title));
                    redirectedPage.Title.Anchor = title.Anchor;
                    redirectedPage.Title.Query = title.Query;
                    title = redirectedPage.Title;
                    if(null != _relToTitle) {

                        // Normalize link if needed (for import/export)
                        addr.RemoveAttr("href");
                        addr.Attr("href.path", title.AsRelativePath(_relToTitle));
                        addr.Attr("href.anchor", title.Anchor);
                        addr.Attr("href.query", title.Query);
                    } else {
                        addr.Attr("href", title.AsEditorUriPath());
                    }
                }
                if(string.IsNullOrEmpty(current.GetAttribute("title"))) {
                    addr.Attr("title", title.IsFile ? title.Filename : title.AsPrefixedUserFriendlyPath());
                }
                addr.RemoveAttr("fileid");
                break;
            case ParserMode.VIEW_NO_EXECUTE:
            case ParserMode.VIEW: {
                    string rel = addr["@rel"].AsText;
                    addr.Attr("rel", "internal");

                    // check if path was generated, if so, keep it as it is
                    if(string.IsNullOrEmpty(rel)) {

                        // check if path is a reference to current page and, if so, make it bold
                        ParserState parseState = GetParseState();
                        foreach(PageBE includingPage in parseState.ProcessingStack) {
                            XDoc item;
                            if((includingPage.Title == title && parseState.ProcessedPages.TryGetValue(GetParserCacheKey(includingPage, _mode), out item) && (item == null))) {
                                XDoc strong = new XDoc("strong");
                                foreach(XmlNode node in addr.AsXmlNode.ChildNodes) {
                                    strong.AsXmlNode.AppendChild(strong.AsXmlNode.OwnerDocument.ImportNode(node, true));
                                }
                                addr.Replace(strong);
                                return;
                            }
                        }
                    }
                }

                // check if link goes to a file attachment
                if(title.IsFile) {

                    // if the file does not exist, display a message accordingly
                    if(0 == fileid) {
                        css.Add("new");
                        if(!containsImage) {
                            var resources = DekiContext.Current.Resources;
                            addr.AddNodesBefore(DekiScriptRuntime.CreateWarningElement(null, "(" + resources.Localize(DekiResources.MISSING_FILE(title.AsPrefixedUserFriendlyPath()) + ")"), null));
                        }
                    } else {
                        if(!containsImage) {
                            css.Add("iconitext-16");
                            css.Add("ext-" + title.Extension);
                        }
                        addr.Attr("href", deki.ApiUri.At("files", fileid.ToString(), Title.AsApiParam(title.Filename)));
                    }
                } else {

                    // check if page exists by first inspecting the links table and, if not found, searching for the page title
                    if(title.IsEditable && !linksKnown.Contains(Title.FromDbPath(title.Namespace, title.Path, null))) {
                        if(linksBroken.Contains(title.AsPrefixedDbPath().ToLowerInvariant())) {
                            css.Add("new");
                        } else {
                            lookupLink = title;
                        }
                    }

                    // check if link goes to a user's page
                    if((title.IsUser) && (1 == title.AsUnprefixedDbSegments().Length)) {
                        css.Add("link-user");
                    }

                    // Update the link to use the site uri
                    addr.Attr("href", Utils.AsPublicUiUri(title));
                }

                if(css.Count > 0 && !containsImage) {
                    css.Add(current.GetAttribute("class"));
                    addr.Attr("class", string.Join(" ", css.ToArray()));
                }
                if(string.IsNullOrEmpty(current.GetAttribute("title"))) {
                    addr.Attr("title", title.IsFile ? title.Filename : title.AsPrefixedUserFriendlyPath());
                }
                addr.RemoveAttr("fileid");
                break;
            }
        }
Beispiel #18
0
        private void ProcessFreeLinks(XmlNode root, DekiContext deki) {

            // check whether the root is an excluded tag
            if(!ExcludedTags.Contains(root) && !root.Name.EqualsInvariantIgnoreCase("a")) {
                for(XmlNode node = root.FirstChild; node != null; node = node.NextSibling) {

                    // seach for free links within text nodes
                    if(XmlNodeType.Text == node.NodeType) {

                        // concatenate adjacent text nodes together
                        while((null != node.NextSibling) && (XmlNodeType.Text == node.NextSibling.NodeType)) {
                            node.Value += node.NextSibling.Value;
                            root.RemoveChild(node.NextSibling);
                        }

                        MatchCollection matches = URL_REGEX.Matches(node.Value);
                        foreach(Match m in matches) {
                            String link = m.Value;

                            // if the url contains an ending parenthesis without a balanced number of starting parenthesis, exclude the remainder of the url
                            int paranthesisCount = 0;
                            int parenthesisIndex = -1;
                            for(int j = 0; j < link.Length; j++) {
                                if('(' == link[j]) {
                                    paranthesisCount++;
                                } else if(')' == link[j]) {
                                    if(0 == paranthesisCount) {
                                        parenthesisIndex = j;
                                        link = link.Substring(0, j);
                                    } else {
                                        paranthesisCount--;
                                    }
                                }
                            }

                            // (bug 7134) avoid making links out of incomplete links
                            XUri uri = XUri.TryParse(link);
                            if((uri != null) && string.IsNullOrEmpty(uri.Authority)) {
                                string pathQueryFragment = uri.PathQueryFragment;
                                if(string.IsNullOrEmpty(pathQueryFragment) || (pathQueryFragment == "/")) {
                                    continue;
                                }
                            }

                            // check if link text needs to be truncated
                            string text = link;
                            if(text.Length >= 54) {
                                text = link.Substring(0, 36) + "..." + link.Substring(link.Length - 14, 14);
                            }

                            // attempt to construct an external link and add it to the document
                            XDoc linkDoc = new XDoc("link").Start("a").Attr("rel", "freelink").Attr("href", link).Value(text).End();
                            if(ProcessExternalLink(linkDoc["a"], _mode, deki)) {
                                XmlText linkAndRemainingText = ((XmlText)node).SplitText(m.Index);
                                XmlText remainingText = linkAndRemainingText.SplitText(link.Length);
                                AddChildrenBefore(remainingText, linkDoc.AsXmlNode);
                                root.RemoveChild(linkAndRemainingText);
                                break;
                            } else if(0 <= parenthesisIndex) {
                                ((XmlText)node).SplitText(m.Index + parenthesisIndex + 1);
                                break;
                            }
                        }
                    } else if(XmlNodeType.Element == node.NodeType) {

                        // recursively process non-text nodes
                        ProcessFreeLinks(node, deki);
                    }
                }
            }
        }