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)); }
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); }
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); } }
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)); }
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)); } }
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(); }
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; }
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(); } }
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; }
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; }
/// <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; }
/// <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; }
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); }
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; } }
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); } } } }