internal static string ProcessCondition(WWSRequest req, DWSConfiguration config, Uri uri, string cond) => Regex.Replace(cond, @"\%\{(?<var>[a-z_]\w*)\}", (Match m) => { switch (m.Groups["var"].ToString().ToLower()) { case "HTTP_USER_AGENT": return(req.UserAgent); case "HTTP_COOKIE": return(req.CookieString); case "HTTP_HOST": return(uri.Host); case "REMOTE_ADDR": return(req.Sender.Address.ToString()); case "REMOTE_HOST": return(Dns.GetHostEntry(req.Sender.Address).HostName); case "REMOTE_USER": case "REMOTE_IDENT": return(req.Sender.ToString()); case "REQUEST_METHOD": return(req.HTTPRequestMethod); case "SCRIPT_FILENAME": return(req.RequestedURLPath); case "QUERY_STRING": return(uri.Query); case "DOCUMENT_ROOT": return(config.Webroot.FullName); case "SERVER_NAME": return(Dns.GetHostEntry(uri.Host).HostName); case "SERVER_ADDR": return(Dns.GetHostEntry(uri.Host).AddressList[0].ToString()); case "SERVER_PORT": return(config.ListeningPort.ToString()); case "SERVER_PROTOCOL": return(config.ServerString); case "SERVER_SOFTWARE": return(Environment.OSVersion.ToString()); case "TIME_YEAR": return(req.UTCRequestTime.Year.ToString()); case "TIME_MON": return(req.UTCRequestTime.Month.ToString()); case "TIME_DAY": return(req.UTCRequestTime.Day.ToString()); case "TIME_HOUR": return(req.UTCRequestTime.Hour.ToString()); case "TIME_MIN": return(req.UTCRequestTime.Minute.ToString()); case "TIME_SEC": return(req.UTCRequestTime.Second.ToString()); case "TIME_WDAY": return(req.UTCRequestTime.DayOfWeek.ToString()); case "TIME": return(WWSDatabaseConnector.SQLEscape(req.UTCRequestTime).Trim('\'')); case "API_VERSION": return("WWS4.0"); case "REQUEST_URI": return(uri.ToString()); case "REQUEST_FILENAME": return(uri.AbsolutePath); default: return($"%{{{m.Groups["var"]}}}"); } }, RegexOptions.Compiled | RegexOptions.IgnoreCase);
/// <summary> /// Processes the given WWS4.0™ Request according to the given rewrite rules and returns the rewrite-engine's result /// </summary> /// <param name="req">Input URI</param> /// <param name="config">The server's configuration</param> /// <param name="rules">Set of HTTP rewrite rules</param> /// <returns>Rewritten URI</returns> public static HTTPRewriteResult ProcessURI(WWSRequest req, DWSConfiguration config, IEnumerable <HTTPRewriteRule> rules) { Regex gen_regex(HTTPRewriteRule rule, out HTTPRewriteFlags[] flags) { flags = (rule.Flags ?? new HTTPRewriteFlags[0]).Distinct().ToArray(); RegexOptions ropt = RegexOptions.CultureInvariant | RegexOptions.Compiled; if (flags.Contains(HTTPRewriteFlags.NC)) { ropt |= RegexOptions.IgnoreCase; } return(new Regex(rule.MatchRegex, ropt)); } rules = (rules ?? new HTTPRewriteRule[0]).Distinct(); Dictionary <string, (string, TimeSpan)> d_coo = new Dictionary <string, (string, TimeSpan)>(); Dictionary <string, string> d_env = new Dictionary <string, string>(); Uri uri = req.RequestedURL; string server_s = null; bool previous = false; bool chained = false; string mime_t = null; int nmaxcnt = 32000; int ncount = 0; int skip = 0; begin: if (rules.Any() && nmaxcnt > ncount) { foreach (HTTPRewriteRule rule in rules) { if (skip == 0) { Regex regex = gen_regex(rule, out HTTPRewriteFlags[] flags); if (!chained || previous) { if (rule.Conditional is string cond) { cond = ProcessCondition(req, config, uri, cond); previous = regex.IsMatch(cond); } else { string qstr = flags.Contains(HTTPRewriteFlags.NQ) ? uri.AbsolutePath : uri.PathAndQuery; qstr = Uri.UnescapeDataString(qstr); if (qstr.StartsWith("/")) { qstr = qstr.Substring(1); } if (regex.IsMatch(qstr)) { string res = rule.OutputExpression; if (res != "-") { res = regex.Replace(qstr, rule.OutputExpression); } bool has_q = res.Contains('?'); if (flags.Contains(HTTPRewriteFlags.QSA) && uri.Query.Length > 0) { res += has_q ? '&' + uri.Query.Substring(1) : uri.Query; } if (!Uri.IsWellFormedUriString(res, UriKind.Absolute)) { res = $"{uri.Scheme}://{uri.Authority}/{(res.StartsWith("/") ? res.Substring(1) : res)}"; } if (uri.Fragment.Length > 0) { res += uri.Fragment; } if (!flags.Contains(HTTPRewriteFlags.BNP)) { res = res.Replace(' ', '+'); } uri = new Uri(res); previous = true; } else { previous = false; } } } chained = flags.Contains(HTTPRewriteFlags.C); if (previous) { foreach (HTTPRewriteFlags f in flags) { if (f is HTTPRewriteFlags.Cookie fco) { d_coo[fco.Name] = (fco.Value, fco.ExpirationDate); } else if (f is HTTPRewriteFlags.EnvironmentVariable fe) { d_env[fe.Name] = fe.Value; } else if (f is HTTPRewriteFlags.ServerString fss) { server_s = fss.String; } else if (f is HTTPRewriteFlags.MimeType ft) { mime_t = ft.Type; } else if (f is HTTPRewriteFlags.Skip fs && fs.Count > 0) { skip = fs.Count; } else if (f is HTTPRewriteFlags.Next fn) { nmaxcnt = Math.Max(nmaxcnt, fn.IterationCount); ++ncount; goto begin; } } if (flags.Contains(HTTPRewriteFlags.L)) { break; } } } else { --skip; } }