internal Yield DeleteSiteLogo(DreamContext context, DreamMessage request, Result <DreamMessage> response) { DekiContext.Current.Instance.Storage.DeleteSiteFile(LOGO_LABEL); ConfigBL.DeleteInstanceSettingsValue(ConfigBL.UI_LOGO_UPLOADED); response.Return(DreamMessage.Ok()); yield break; }
public bool MimeTypeCanBeInlined(MimeType mimeType) { if (_inlineDispositionBlacklist == null) { string[] blackliststrings = ConfigBL.GetInstanceSettingsValue("files/blacklisted-disposition-mimetypes", string.Empty).ToLowerInvariant().Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); List <MimeType> blacklist = new List <MimeType>(); foreach (string v in blackliststrings) { MimeType mt = MimeType.New(v); if (mt != null) { blacklist.Add(mt); } } string[] whiteliststrings = ConfigBL.GetInstanceSettingsValue("files/whitelisted-disposition-mimetypes", string.Empty).ToLowerInvariant().Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); List <MimeType> whitelist = new List <MimeType>(); foreach (string v in whiteliststrings) { MimeType mt = MimeType.New(v); if (mt != null) { whitelist.Add(mt); } } _inlineDispositionBlacklist = blacklist; _inlineDispositionWhitelist = whitelist; } foreach (MimeType bad in _inlineDispositionBlacklist) { if (mimeType.Match(bad)) { return(false); } } foreach (MimeType good in _inlineDispositionWhitelist) { if (mimeType.Match(good)) { return(true); } } return(false); }
internal Yield PutSiteLogo(DreamContext context, DreamMessage request, Result <DreamMessage> response) { //Confirm image type if (!new MimeType("image/*").Match(request.ContentType)) { throw new SiteImageMimetypeInvalidArgumentException(); } try { //Save file to storage provider DekiContext.Current.Instance.Storage.PutSiteFile(LOGO_LABEL, new StreamInfo(request.AsStream(), request.ContentLength, request.ContentType)); ConfigBL.SetInstanceSettingsValue(ConfigBL.UI_LOGO_UPLOADED, "true"); } catch (Exception x) { DekiContext.Current.Instance.Log.Warn("Failed to save logo to storage provider", x); ConfigBL.DeleteInstanceSettingsValue(ConfigBL.UI_LOGO_UPLOADED); throw; } StreamInfo file = DekiContext.Current.Instance.Storage.GetSiteFile(LOGO_LABEL, false); if (file != null) { StreamInfo thumb = AttachmentPreviewBL.BuildThumb(file, FormatType.PNG, RatioType.UNDEFINED, DekiContext.Current.Instance.LogoWidth, DekiContext.Current.Instance.LogoHeight); if (thumb != null) { DekiContext.Current.Instance.Storage.PutSiteFile(LOGO_LABEL, thumb); } else { DekiContext.Current.Instance.Log.WarnMethodCall("PUT:site/logo", "Unable to process logo through imagemagick"); DekiContext.Current.ApiPlug.At("site", "logo").Delete(); throw new SiteUnableToProcessLogoInvalidOperationException(); } } else { DekiContext.Current.Instance.Log.WarnMethodCall("PUT:site/logo", "Unable to retrieve saved logo"); } response.Return(DreamMessage.Ok()); yield break; }
internal Yield PutSiteSettings(DreamContext context, DreamMessage request, Result <DreamMessage> response) { if (!MimeType.XML.Match(request.ContentType)) { throw new SiteExpectedXmlContentTypeInvalidArgumentException(); } var dekiContext = DekiContext.Current; XDoc settings = request.ToDocument(); ConfigBL.SetInstanceSettings(settings); dekiContext.Instance.EventSink.InstanceSettingsChanged(DekiContext.Current.Now); yield return(Coroutine.Invoke(ConfigureMailer, settings, new Result()).CatchAndLog(_log)); // clear out the digital signature key dekiContext.Instance.PrivateDigitalSignature = null; // clear out banned words dekiContext.Instance.BannedWords = null; response.Return(DreamMessage.Ok()); yield break; }
internal Yield GetSiteSettings(DreamContext context, DreamMessage request, Result <DreamMessage> response) { var givenKey = context.GetParam("apikey", null); var includes = context.GetParam("include", ""); //If apikey is not given for a request, dont return hidden entries var validMasterKey = MasterApiKey.EqualsInvariant(givenKey); var retrieve = new SiteSettingsRetrievalSettings { IncludeHidden = validMasterKey, IncludeAnonymousUser = includes.Contains(UserBL.ANON_USERNAME), IncludeLicense = (validMasterKey || PermissionsBL.IsUserAllowed(DekiContext.Current.User, Permissions.ADMIN)) && includes.Contains(ConfigBL.LICENSE), }; var doc = ConfigBL.GetInstanceSettingsAsDoc(retrieve); // check if a custom logo was uploaded; if yes, update the config document. // This is being done outside of caching since URIs to the api should be computed for every request if (doc[ConfigBL.UI_LOGO_UPLOADED].AsBool ?? false) { doc.InsertValueAt(ConfigBL.UI_LOGO_URI, DekiContext.Current.ApiUri.At("site", "logo.png").ToString()); } response.Return(DreamMessage.Ok(doc)); yield break; }
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; }
/// <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; }