예제 #1
0
        public Yield GetFileRevisions(DreamContext context, DreamMessage request, Result <DreamMessage> response)
        {
            CheckResponseCache(context, false);

            //Default change filter is CONTENT changes to preserve backwards compat
            string changeFilterStr = context.GetParam("changefilter", AttachmentBL.DEFAULT_REVISION_FILTER.ToString());

            ResourceBE.ChangeOperations changeFilter = ResourceBE.ChangeOperations.UNDEFINED;
            if (!string.IsNullOrEmpty(changeFilterStr))
            {
                if (StringUtil.EqualsInvariantIgnoreCase("all", changeFilterStr))
                {
                    changeFilter = ResourceBE.ChangeOperations.UNDEFINED;
                }
                else if (!SysUtil.TryParseEnum(changeFilterStr, out changeFilter))
                {
                    throw new DreamBadRequestException("changefilter value is invalid. Possible values are ALL, " + string.Join(",", Enum.GetNames(typeof(ResourceBE.ChangeOperations))));
                }
            }

            PageBE     parentPage   = null;
            ResourceBE fileRevision = GetAttachment(context, request, Permissions.READ, false, false, out parentPage);
            XUri       listUri      = AttachmentBL.Instance.GetUri(fileRevision).At("revisions").With("changefilter", changeFilterStr.ToLowerInvariant());
            XDoc       ret          = AttachmentBL.Instance.GetFileRevisionsXml(fileRevision, changeFilter, listUri, fileRevision.Revision);

            response.Return(DreamMessage.Ok(ret));
            yield break;
        }
예제 #2
0
        public Yield GetTags(DreamContext context, DreamMessage request, Result <DreamMessage> response)
        {
            string type        = DreamContext.Current.GetParam("type", "");
            string fromStr     = DreamContext.Current.GetParam("from", "");
            string toStr       = DreamContext.Current.GetParam("to", "");
            bool   showPages   = DreamContext.Current.GetParam("pages", false);
            string partialName = DreamContext.Current.GetParam("q", "");

            // parse type
            TagType tagType = TagType.ALL;

            if (!string.IsNullOrEmpty(type) && !SysUtil.TryParseEnum(type, out tagType))
            {
                throw new DreamBadRequestException("Invalid type parameter");
            }

            // check and validate from date
            DateTime from = (tagType == TagType.DATE) ? DateTime.Now : DateTime.MinValue;

            if (!string.IsNullOrEmpty(fromStr) && !DateTime.TryParse(fromStr, out from))
            {
                throw new DreamBadRequestException("Invalid from date parameter");
            }

            // check and validate to date
            DateTime to = (tagType == TagType.DATE) ? from.AddDays(30) : DateTime.MaxValue;

            if (!string.IsNullOrEmpty(toStr) && !DateTime.TryParse(toStr, out to))
            {
                throw new DreamBadRequestException("Invalid to date parameter");
            }

            // execute query
            var  tagBL = new TagBL();
            var  tags  = tagBL.GetTags(partialName, tagType, from, to);
            XDoc doc   = tagBL.GetTagListXml(tags, "tags", null, showPages);

            response.Return(DreamMessage.Ok(doc));
            yield break;
        }
예제 #3
0
        protected override Yield Start(XDoc config, IContainer container, Result result)
        {
            yield return(Coroutine.Invoke(base.Start, config, new Result()));

            // ensure imagemagick is setup correctly.
            if (string.IsNullOrEmpty(ImageMagickConvertPath))
            {
                throw new NotImplementedException("Please set 'imagemagick-convert-path' in config to path of ImageMagick's 'convert'");
            }
            if (!File.Exists(ImageMagickIdentifyPath))
            {
                throw new FileNotFoundException("Cannot find ImagicMagick 'identify' binary: ", ImageMagickIdentifyPath);
            }
            if (string.IsNullOrEmpty(ImageMagickIdentifyPath))
            {
                throw new NotImplementedException("Please set 'imagemagick-identify-path' in config to path of ImageMagick's 'identify'");
            }
            if (!File.Exists(ImageMagickConvertPath))
            {
                throw new FileNotFoundException("Cannot find ImagicMagick 'convert' binary: ", ImageMagickConvertPath);
            }

            // check for 'apikey'
            _apikey = Config["api-key"].AsText ?? Config["apikey"].AsText;
            if (string.IsNullOrEmpty(_apikey))
            {
                throw new ArgumentNullException("apikey", "The global apikey is not defined. Please ensure that you have a global <apikey> defined in the MindTouch Core service settings xml file.");
            }
            InitializeContainer(container);

            // intialize instance manager
            _instanceManager = InstanceManager.New(this, this.TimerFactory);

            // setup resource manager
            lock (SyncRoot) {
                if (ResourceManager == null)
                {
                    ResourceManager = new PlainTextResourceManager(ResourcesPath);
                    ScreenFont      = new DekiFont(Plug.New("resource://mindtouch.deki/MindTouch.Deki.Resources.Arial.mtdf").Get().AsBytes());
                }
            }

            // initialize scripting engine
            XDoc scripting = Config["scripting"];

            DekiScriptLibrary.InsertTextLimit = scripting["max-web-response-length"].AsLong ?? DekiScriptLibrary.InsertTextLimit;
            DekiScriptLibrary.MinCacheTtl     = scripting["min-web-cache-ttl"].AsDouble ?? DekiScriptLibrary.MinCacheTtl;

            // set up deki pub sub (by default we override uri.publish with our own service, unless @must-use=true is specified)
            if (!(Config["uri.publish/@must-use"].AsBool ?? false))
            {
                Result <Plug> pubsubResult;
                XDoc          pubsubConfig = new XDoc("config")
                                             .Elem("uri.deki", Self.Uri.With("apikey", MasterApiKey))
                                             .Start("downstream")
                                             .Elem("uri", PubSub.At("publish").Uri.WithoutLastSegment().At("subscribers"))
                                             .End()
                                             .Start("components")
                                             .Start("component")
                                             .Attr("type", typeof(IPubSubDispatcher).AssemblyQualifiedName)
                                             .Attr("implementation", typeof(DekiDispatcher).AssemblyQualifiedName)
                                             .End()
                                             .End()
                                             .Elem("authtoken", MasterApiKey);
                foreach (var cookie in Cookies.Fetch(PubSub.Uri))
                {
                    pubsubConfig.Add(cookie.AsSetCookieDocument);
                }
                var messageQueuePath = config["publish/queue-path"].AsText;
                if (!string.IsNullOrEmpty(messageQueuePath))
                {
                    pubsubConfig.Elem("queue-path", messageQueuePath);
                }
                yield return(pubsubResult = CreateService(
                                 "pubsub",
                                 "sid://mindtouch.com/dream/2008/10/pubsub",
                                 pubsubConfig,
                                 new Result <Plug>()));

                PubSub = pubsubResult.Value;
            }

            // set up package updater service (unless it was passed in)
            XUri packageUpdater;

            if (config["packageupdater/@uri"].IsEmpty)
            {
                var packageConfig = config["packageupdater"];
                packageConfig = packageConfig.IsEmpty ? new XDoc("config") : packageConfig.Clone();
                if (packageConfig["package-path"].IsEmpty)
                {
                    packageConfig.Elem("package-path", Path.Combine(Path.Combine(config["deki-path"].AsText, "packages"), "default"));
                }
                yield return(CreateService(
                                 "packageupdater",
                                 "sid://mindtouch.com/2010/04/packageupdater",
                                 new XDoc("config")
                                 .Elem("apikey", MasterApiKey)
                                 .AddNodes(packageConfig),
                                 new Result <Plug>()
                                 ));

                packageUpdater = Self.Uri.At("packageupdater");
            }
            else
            {
                packageUpdater = config["packageupdater/@uri"].AsUri;
            }
            _packageUpdater = Plug.New(packageUpdater);

            // set up emailer service (unless it was passed in)
            XUri mailerUri;

            if (config["uri.mailer"].IsEmpty)
            {
                yield return(CreateService(
                                 "mailer",
                                 "sid://mindtouch.com/2009/01/dream/email",
                                 new XDoc("config")
                                 .Elem("apikey", MasterApiKey)
                                 .AddAll(Config["smtp/*"]),
                                 new Result <Plug>()
                                 ));

                mailerUri = Self.Uri.At("mailer");
            }
            else
            {
                mailerUri = config["uri.mailer"].AsUri;
            }
            _mailer = Plug.New(mailerUri);

            // set up the email subscription service (unless it was passed in)
            XUri pageSubscription;

            if (config["uri.page-subscription"].IsEmpty)
            {
                XDoc pagesubserviceConfig = new XDoc("config")
                                            .Elem("uri.deki", Self.Uri)
                                            .Elem("uri.emailer", mailerUri.At("message"))
                                            .Elem("resources-path", ResourcesPath)
                                            .Elem("apikey", MasterApiKey)
                                            .Start("components")
                                            .Start("component")
                                            .Attr("scope", "factory")
                                            .Attr("type", typeof(IPageSubscriptionDataSessionFactory).AssemblyQualifiedName)
                                            .Attr("implementation", "MindTouch.Deki.Data.MySql.UserSubscription.MySqlPageSubscriptionSessionFactory, mindtouch.deki.data.mysql")
                                            .End()
                                            .End()
                                            .AddAll(Config["page-subscription/*"]);
                foreach (var cookie in Cookies.Fetch(mailerUri))
                {
                    pagesubserviceConfig.Add(cookie.AsSetCookieDocument);
                }
                yield return(CreateService(
                                 "pagesubservice",
                                 "sid://mindtouch.com/deki/2008/11/changesubscription",
                                 pagesubserviceConfig,
                                 new Result <Plug>()
                                 ));

                pageSubscription = Self.Uri.At("pagesubservice");
                config.Elem("uri.page-subscription", pageSubscription);
            }
            else
            {
                pageSubscription = config["uri.page-subscription"].AsUri;
            }
            _pageSubscription = Plug.New(pageSubscription);

            // set up package importer, if not provided
            if (Config["uri.package"].IsEmpty)
            {
                yield return(CreateService(
                                 "package",
                                 "sid://mindtouch.com/2009/07/package",
                                 new XDoc("config").Elem("uri.deki", Self.Uri),
                                 new Result <Plug>()));

                Config.Elem("uri.package", Self.Uri.At("package"));
            }

            // set up lucene
            _luceneIndex = Plug.New(Config["indexer/@src"].AsUri);
            if (_luceneIndex == null)
            {
                // create the indexer service
                XDoc luceneIndexConfig = new XDoc("config")
                                         .AddNodes(Config["indexer"])
                                         .Start("apikey").Attr("hidden", true).Value(MasterApiKey).End();
                if (luceneIndexConfig["path.store"].IsEmpty)
                {
                    luceneIndexConfig.Elem("path.store", Path.Combine(Path.Combine(config["deki-path"].AsText, "luceneindex"), "$1"));
                }
                yield return(CreateService("luceneindex", SID_FOR_LUCENE_INDEX, luceneIndexConfig, new Result <Plug>()).Set(v => _luceneIndex = v));

                _isLocalLuceneService = true;
            }
            else
            {
                // push our host's pubsub service to lucene, to keep it up to date on our changes
                var pubsub = new XDoc("pubsub").Attr("href", PubSub);
                foreach (var cookie in PubSub.CookieJar.Fetch(PubSub.Uri))
                {
                    pubsub.Add(cookie.AsSetCookieDocument);
                }
                yield return(_luceneIndex.At("subscriptions").PostAsync(pubsub));
            }

            // configure indexing whitelist
            _indexNamespaceWhitelist = new[] { NS.MAIN, NS.PROJECT, NS.USER, NS.TEMPLATE, NS.HELP, NS.MAIN_TALK, NS.PROJECT_TALK, NS.USER_TALK, NS.TEMPLATE_TALK, NS.HELP_TALK, NS.SPECIAL, NS.SPECIAL_TALK };
            if (!string.IsNullOrEmpty(Config["indexer/namespace-whitelist"].AsText))
            {
                List <NS> customWhitelist = new List <NS>();
                foreach (string item in Config["indexer/namespace-whitelist"].AsText.Split(','))
                {
                    NS ns;
                    if (SysUtil.TryParseEnum(item, out ns))
                    {
                        customWhitelist.Add(ns);
                    }
                }
                _indexNamespaceWhitelist = customWhitelist.ToArray();
            }

            if (!Config["wikis/globalconfig/cache/varnish"].IsEmpty)
            {
                // create the varnish service

                // TODO (petee): getting the varnish config from wikis/globalconfig/cache is a hack
                // The frontend needs to get the max-age to send out the cache headers but we currently have no way
                // of getting the DekiWikiService config so we'll hack it so it comes back in GET:site/settings.
                XDoc varnishConfig = new XDoc("config")
                                     .Elem("uri.deki", Self.Uri.With("apikey", MasterApiKey))
                                     .Elem("uri.varnish", Config["wikis/globalconfig/cache/varnish"].AsUri)
                                     .Elem("varnish-purge-delay", Config["wikis/globalconfig/cache/varnish-purge-delay"].AsInt ?? 10)
                                     .Elem("varnish-max-age", Config["wikis/globalconfig/cache/varnish-max-age"].AsInt ?? 300)
                                     .Start("apikey").Attr("hidden", true).Value(MasterApiKey).End();
                yield return(CreateService("varnish", SID_FOR_VARNISH_SERVICE, varnishConfig, new Result <Plug>()));
            }
            _isInitialized = true;
            result.Return();
        }
예제 #4
0
        public Yield GetFile(DreamContext context, DreamMessage request, Result <DreamMessage> response)
        {
            PageBE       parentPage   = null;
            DreamMessage responseMsg  = null;
            ResourceBE   fileRevision = GetAttachment(context, request, Permissions.READ, true, false, out parentPage);

            if (fileRevision.IsHidden)
            {
                PermissionsBL.CheckUserAllowed(DekiContext.Current.User, Permissions.ADMIN);
            }

            // check if only file information is requested
            if (context.Verb == Verb.HEAD)
            {
                response.Return(new DreamMessage(DreamStatus.Ok, null, fileRevision.MimeType, (long)fileRevision.Size, Stream.Null));
                yield break;
            }
            try {
                if (request.CheckCacheRevalidation(fileRevision.Timestamp))
                {
                    responseMsg = DreamMessage.NotModified();
                }
                if (responseMsg == null)
                {
                    #region Preview related parameter parsing
                    string sFormat    = context.GetParam("format", string.Empty);
                    string sRatio     = context.GetParam("ratio", string.Empty);
                    uint   height     = context.GetParam <uint>("height", 0);
                    uint   width      = context.GetParam <uint>("width", 0);
                    string cachedSize = context.GetParam("size", string.Empty);

                    // check 'ratio' parameter
                    RatioType ratio = RatioType.UNDEFINED;
                    if (!string.IsNullOrEmpty(sRatio))
                    {
                        switch (sRatio.ToLowerInvariant().Trim())
                        {
                        case "var":
                        case "variable":
                            ratio = RatioType.VARIABLE;
                            break;

                        case "fixed":
                            ratio = RatioType.FIXED;
                            break;

                        default:
                            throw new AttachmentFileRatioInvalidArgumentException();
                        }
                    }

                    // check 'size' parameter
                    SizeType size = SizeType.UNDEFINED;
                    if (!string.IsNullOrEmpty(cachedSize) && !SysUtil.TryParseEnum(cachedSize.Trim(), out size))
                    {
                        throw new AttachmentFilesizeInvalidArgumentException();
                    }

                    // check 'format' parameter
                    FormatType format = FormatType.UNDEFINED;
                    if (!string.IsNullOrEmpty(sFormat) && !SysUtil.TryParseEnum(sFormat.Trim(), out format))
                    {
                        throw new AttachmentFileFormatInvalidArgumentException();
                    }
                    #endregion

                    //if any preview related parameters are set, do preview logic. Otherwise return the file
                    StreamInfo file = null;
                    if ((size != SizeType.UNDEFINED && size != SizeType.ORIGINAL) ||
                        ratio != RatioType.UNDEFINED ||
                        format != FormatType.UNDEFINED ||
                        height != 0 ||
                        width != 0
                        )
                    {
                        file = AttachmentPreviewBL.RetrievePreview(fileRevision, height, width, ratio, size, format);
                    }
                    else
                    {
                        var isMSWebDAV = MSWEBDAV_USER_AGENT_REGEX.IsMatch(request.Headers.UserAgent ?? string.Empty);
                        file = DekiContext.Current.Instance.Storage.GetFile(fileRevision, SizeType.ORIGINAL, !isMSWebDAV);
                    }

                    // prepare response
                    if (file == null)
                    {
                        throw new AttachmentDoesNotExistFatalException(fileRevision.ResourceId, fileRevision.Revision);
                    }

                    if (file.Uri != null)
                    {
                        responseMsg = DreamMessage.Redirect(file.Uri);
                    }
                    else
                    {
                        bool inline = fileRevision.MetaXml.ImageHeight.HasValue;

                        // see if we can use the MimeType map for allowing inlining
                        if (!inline)
                        {
                            // if IE inline security is not disabled
                            bool isIE = false;
                            if (!DekiContext.Current.Instance.EnableUnsafeIEContentInlining)
                            {
                                // check the user agent to see if we're dealing with IE
                                isIE = MSIE_USER_AGENT_REGEX.IsMatch(request.Headers.UserAgent ?? string.Empty);
                            }

                            // see if the mime-type could allow inlining
                            inline = DekiContext.Current.Instance.MimeTypeCanBeInlined(fileRevision.MimeType);
                            if (inline && isIE)
                            {
                                // check whether the creator of the file had unsafecontent permission, to override IE security
                                IList <ResourceBE> revisions         = ResourceBL.Instance.GetResourceRevisions(fileRevision.ResourceId, ResourceBE.ChangeOperations.CONTENT, SortDirection.DESC, 1);
                                UserBE             lastContentEditor = UserBL.GetUserById(revisions[0].UserId);
                                inline = PermissionsBL.IsUserAllowed(lastContentEditor, parentPage, Permissions.UNSAFECONTENT);
                            }
                        }
                        responseMsg = DreamMessage.Ok(fileRevision.MimeType, file.Length, file.Stream);
                        responseMsg.Headers["X-Content-Type-Options"] = "nosniff";
                        responseMsg.Headers.ContentDisposition        = new ContentDisposition(inline, fileRevision.Timestamp, null, null, fileRevision.Name, file.Length, request.Headers.UserAgent);

                        // MSIE6 will delete a downloaded file before the helper app trying to use it can get to it so we
                        //have to do custom cache control headers for MSIE6 so that the file can actually be opened
                        if (MSIE6_USER_AGENT_REGEX.IsMatch(request.Headers.UserAgent ?? string.Empty))
                        {
                            responseMsg.Headers["Expires"]   = "0";
                            responseMsg.Headers.Pragma       = "cache";
                            responseMsg.Headers.CacheControl = "private";
                        }
                        else
                        {
                            responseMsg.SetCacheMustRevalidate(fileRevision.Timestamp);
                        }
                    }
                }
            } catch {
                if (responseMsg != null)
                {
                    responseMsg.Close();
                }
                throw;
            }
            response.Return(responseMsg);
            yield break;
        }
예제 #5
0
        private DekiScriptInvocationTargetDescriptor ConvertFunction(XDoc function)
        {
            string functionName = function["name"].AsText;

            if (string.IsNullOrEmpty(functionName))
            {
                _log.WarnFormat("function without name in script {0}; skipping function definition", _manifestUri);
                return(null);
            }

            // determine function access level
            DreamAccess access;

            switch (function["access"].AsText ?? "public")
            {
            case "private":
                access = DreamAccess.Private;
                break;

            case "internal":
                access = DreamAccess.Internal;
                break;

            case "public":
                access = DreamAccess.Public;
                break;

            default:
                _log.WarnFormat("unrecognized access level '{0}' for function {1} in script {2}; defaulting to public", function["access"].AsText, functionName, _manifestUri);
                access = DreamAccess.Public;
                break;
            }

            // convert parameters
            List <DekiScriptParameter> parameters = new List <DekiScriptParameter>();

            foreach (XDoc param in function["param"])
            {
                string paramName = param["@name"].AsText;

                // determine if parameter has a default value
                string            paramDefault           = param["@default"].AsText;
                DekiScriptLiteral paramDefaultExpression = DekiScriptNil.Value;
                bool paramOptional = false;
                if (paramDefault != null)
                {
                    paramOptional = true;
                    try {
                        paramDefaultExpression = ScriptRuntime.Evaluate(DekiScriptParser.Parse(Location.Start, paramDefault), DekiScriptEvalMode.Evaluate, ScriptRuntime.CreateEnv());
                    } catch (Exception e) {
                        _log.ErrorExceptionFormat(e, "invalid default value for parameter {0} in function {1} in script {2}; skipping function definition", paramName, functionName, _manifestUri);
                        return(null);
                    }
                }
                else
                {
                    paramOptional = (param["@optional"].AsText == "true");
                }

                // determine parameter type
                string         paramType = param["@type"].AsText ?? "any";
                DekiScriptType paramScriptType;
                if (!SysUtil.TryParseEnum(paramType, out paramScriptType))
                {
                    _log.WarnFormat("unrecognized param type '{0}' for parameter {1} in function {2} in script {3}; defaulting to any", paramType, paramName, functionName, _manifestUri);
                    paramScriptType = DekiScriptType.ANY;
                }

                // add parameter
                parameters.Add(new DekiScriptParameter(paramName, paramScriptType, paramOptional, param.Contents, typeof(object), paramDefaultExpression));
            }
            var parameterArray = parameters.ToArray();

            // determine function body
            XDoc   ret  = function["return"];
            string src  = ret["@src"].AsText;
            string type = ret["@type"].AsText;
            DekiScriptExpression expression;

            if (!string.IsNullOrEmpty(src))
            {
                // 'src' attribute is set, load the script from it
                XDoc script;
                if (_manifestUri != null)
                {
                    // check if uri is relative
                    XUri scriptUri = XUri.TryParse(src) ?? _manifestUri.AtPath(src);
                    script = Plug.New(scriptUri).Get().ToDocument();
                }
                else
                {
                    // check if filename is relative
                    if (!Path.IsPathRooted(src))
                    {
                        src = Path.Combine(_resourcesPath, src);
                    }
                    script = XDocFactory.LoadFrom(src, MimeType.XML);
                }
                expression = DekiScriptParser.Parse(script);
                type       = type ?? "xml";
            }
            else if (!ret["html"].IsEmpty)
            {
                // <return> element contains a <html> node; parse it as a script
                expression = DekiScriptParser.Parse(ret["html"]);
                type       = type ?? "xml";
            }
            else if (!ret.IsEmpty)
            {
                // <return> element contains something else; use the text contents as deki-script expression
                var location = new Location(string.Format("/function[name={0}]/return", functionName));
                expression = DekiScriptParser.Parse(location, function["return"].AsText ?? string.Empty);
                expression = DekiScriptExpression.ReturnScope(location, expression);
                type       = type ?? "any";
            }
            else
            {
                _log.WarnFormat("function {0} has no body in script {1}; skipping function definition", functionName, _manifestUri);
                return(null);
            }

            // determine return type
            DekiScriptType returnScriptType;

            if (!SysUtil.TryParseEnum(type, out returnScriptType))
            {
                _log.WarnFormat("unrecognized return type '{0}' for function {1} in script {2}; defaulting to any", type, functionName, _manifestUri);
                returnScriptType = DekiScriptType.ANY;
            }

            // create function descriptor
            var    target      = new DekiScriptScriptFunctionInvocationTarget(access, parameterArray, expression, _commonEnv, returnScriptType);
            string description = function["description"].AsText;
            string transform   = function["@transform"].AsText;

            return(new DekiScriptInvocationTargetDescriptor(access, false, false, functionName, parameterArray, returnScriptType, description, transform, target));
        }