Skip to content

Commit c743a91

Browse files
authored
Release 0.17.4
2 parents d4a6cda + 83b4d92 commit c743a91

File tree

8 files changed

+213
-69
lines changed

8 files changed

+213
-69
lines changed

ConfigFileLoader.cs

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ public static void LoadInclude(string IncludeString)
8787
}
8888
if (ConfigFileName.StartsWith("./") || ConfigFileName.StartsWith(@".\")) DefaultConfigDir = ".";
8989
if (!string.IsNullOrEmpty(CustomConfigFile)) DefaultConfigDir = new FileInfo(CustomConfigFile).DirectoryName;
90-
Includable = Includable.Replace("%WOConfigDir%", DefaultConfigDir);
9190
Variables["WOConfigDir"] = DefaultConfigDir;
91+
Includable = ExpandMaskedVariables(Includable);
9292

9393
if (Includable.Contains('*') || Includable.Contains('?')) //it's a file mask
9494
{
@@ -169,7 +169,12 @@ public static void ProcessConfiguration()
169169
switch (Option.Key)
170170
{
171171
case "Port":
172-
ConfigFile.Port = Convert.ToInt32(Option.Value);
172+
string Port = ExpandMaskedVariables(Option.Value);
173+
if (!int.TryParse(Port, out ConfigFile.Port))
174+
{ throw new ArgumentOutOfRangeException("Port", "\"" + Port + "\" is not a TCP/IP port number."); }
175+
176+
if (ConfigFile.Port < IPEndPoint.MinPort || ConfigFile.Port > IPEndPoint.MaxPort)
177+
{ throw new ArgumentOutOfRangeException("Port", "\"" + Port + "\" is out of the range of valid values."); }
173178
break;
174179
case "Port2":
175180
case "HttpPort":
@@ -178,7 +183,7 @@ public static void ProcessConfiguration()
178183
Log.WriteLine(true, false, "Warning: Use of '{0}' is deprecated, use 'Port' at {1}.", Option.Key, Option.Location);
179184
break;
180185
case "OutputEncoding":
181-
ConfigFile.OutputEncoding = GetCodePage(Option.Value);
186+
ConfigFile.OutputEncoding = GetCodePage(ExpandMaskedVariables(Option.Value));
182187
break;
183188
case "Authenticate":
184189
ConfigFile.Authenticate.Add(Option.Value);
@@ -216,20 +221,30 @@ public static void ProcessConfiguration()
216221
ConfigFile.UserAgent = Option.Value;
217222
break;
218223
case "DefaultHostName":
219-
ConfigFile.DefaultHostName = Option.Value.Replace("%HostName%", Environment.MachineName);
224+
Dictionary<string, string> SuggestedHostNames = new();
225+
int LanNo = 0;
226+
foreach (IPAddress LocIP in Program.GetLocalIPAddresses())
227+
{
228+
SuggestedHostNames.Add("IP" + LanNo, LocIP.ToString());
229+
LanNo++;
230+
}
231+
SuggestedHostNames.Add("HostName", Environment.MachineName);
232+
ConfigFile.DefaultHostName = ExpandMaskedVariables(Option.Value, SuggestedHostNames);
233+
220234
bool ValidHostName = (Environment.MachineName.ToLower() == ConfigFile.DefaultHostName.ToLower());
221-
if (!ValidHostName) foreach (System.Net.IPAddress LocIP in Program.GetLocalIPAddresses())
235+
if (!ValidHostName) foreach (IPAddress LocIP in Program.GetLocalIPAddresses())
222236
{ if (LocIP.ToString() == ConfigFile.DefaultHostName) ValidHostName = true; }
223237
if (!ValidHostName)
224-
{ try { if (System.Net.Dns.GetHostEntry(ConfigFile.DefaultHostName).AddressList.Count() > 0) ValidHostName = true; } catch { } }
225-
if (!ValidHostName) Log.WriteLine(true, false, "Warning: DefaultHostName setting is not applicable to this computer!");
238+
{ try { if (Dns.GetHostEntry(ConfigFile.DefaultHostName).AddressList.Count() > 0) ValidHostName = true; } catch { } }
239+
if (!ValidHostName) Log.WriteLine(true, false, "Warning: DefaultHostName setting is not applicable to this computer.");
226240
break;
227241
case "ValidateCertificates":
228242
ConfigFile.ValidateCertificates = ToBoolean(Option.Value);
229243
break;
230244
case "TemporaryDirectory":
231-
if (Option.Value.ToUpper() == "%TEMP%" || Option.Value == "$TEMP" || Option.Value == "$TMPDIR") ConfigFile.TemporaryDirectory = Path.GetTempPath();
232-
else ConfigFile.TemporaryDirectory = Option.Value;
245+
ConfigFile.TemporaryDirectory = ExpandMaskedVariables(Option.Value);
246+
if (!Directory.Exists(ConfigFile.TemporaryDirectory))
247+
Log.WriteLine(true, false, "Warning: Temporary directory {0} does not exists.", Option.Value);
233248
break;
234249
case "LogFile":
235250
if (Program.OverrideLogFile != null && Program.OverrideLogFile == "")
@@ -240,7 +255,7 @@ public static void ProcessConfiguration()
240255
LogAgent.OpenLogFile(Program.GetLogFilePath(Option.Value.Replace(@"\\", @"\").Replace("//", "/")), true);
241256
break;
242257
case "DisplayStatusPage":
243-
ConfigFile.DisplayStatusPage = Option.Value;
258+
ConfigFile.DisplayStatusPage = ExpandMaskedVariables(Option.Value);
244259
break;
245260
case "ArchiveDateLimit":
246261
int ArchiveDateLimit = 0;
@@ -256,7 +271,7 @@ public static void ProcessConfiguration()
256271
Log.WriteLine(true, false, "Warning: The ArchiveDateLimit must be in YYYYMMDD format.");
257272
break;
258273
case "UpperProxy":
259-
ConfigFile.UpperProxy = Option.Value;
274+
ConfigFile.UpperProxy = ExpandMaskedVariables(Option.Value);
260275
break;
261276
case "PageStyleHtml":
262277
ConfigFile.PageStyleHtml = Option.Value;
@@ -283,13 +298,14 @@ public static void ProcessConfiguration()
283298
ConfigFile.EnableManualConverting = ToBoolean(Option.Value);
284299
break;
285300
case "ContentDirectory":
286-
string AltContentDirName = new FileInfo(AppContext.BaseDirectory).DirectoryName + Option.Value;
287-
if (Directory.Exists(Option.Value))
288-
ConfigFile.ContentDirectory = Option.Value;
301+
string ContentDirName = ExpandMaskedVariables(Option.Value);
302+
string AltContentDirName = new FileInfo(AppContext.BaseDirectory).DirectoryName + ContentDirName;
303+
if (Directory.Exists(ContentDirName))
304+
ConfigFile.ContentDirectory = ContentDirName;
289305
else if (Directory.Exists(AltContentDirName))
290306
ConfigFile.ContentDirectory = AltContentDirName;
291307
else
292-
Log.WriteLine(true, false, "Warning: Incorrect ContentDirectory '{0}'.", Option.Value);
308+
Log.WriteLine(true, false, "Warning: Incorrect ContentDirectory '{0}'.", ContentDirName);
293309
break;
294310
default:
295311
Log.WriteLine(true, false, "Warning: Unknown server option {0} in {1}.", Option.Key, Option.Location);
@@ -357,7 +373,7 @@ public static void ProcessConfiguration()
357373
case "HostNames":
358374
foreach (ConfigFileOption Line in Section.Options)
359375
{
360-
ConfigFile.HostNames.Add(Line.RawString);
376+
ConfigFile.HostNames.Add(ExpandMaskedVariables(Line.RawString));
361377
}
362378
break;
363379
case "Authenticate":
@@ -515,7 +531,7 @@ public static void ProcessConfiguration()
515531
if (!Option.Value.Contains("CN="))
516532
Log.WriteLine(true, false, "Warning: '{0}' is not a valid X.500 distinguished subject name, at {1}.", Option.Value, Option.Location);
517533
else
518-
ConfigFile.SslRootSubject = Option.Value;
534+
ConfigFile.SslRootSubject = ExpandMaskedVariables(Option.Value);
519535
break;
520536
case "SslRootValidAfter":
521537
ConfigFile.SslRootValidAfter = ToDateTimeOffset(Option.Value);

HttpOperation.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ internal void SendRequest()
139139

140140
try
141141
{
142-
var resp = Program.HTTPClient.SendAsync(Request);
142+
var resp = Program.HTTPClient.SendAsync(Request, HttpCompletionOption.ResponseHeadersRead);
143143
resp.Wait();
144144
Response = resp.Result;
145145
//Response = Program.HTTPClient.Send(Request);

HttpTransit.cs

Lines changed: 85 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -848,10 +848,12 @@ private void SendInternalPage(string InternalPageId, string Arguments)
848848
case "/":
849849
case "/!":
850850
case "/!/":
851+
// Status page
851852
SendInternalStatusPage();
852853
return;
853854
case "/!codepages":
854855
case "/!codepages/":
856+
// Code page list
855857
string codepages = "<p>The following code pages are available: <br>\n" +
856858
"<table><tr><td><b>Name</b></td><td><b>#</b></td><td><b>Description</b></td></tr>\n";
857859
codepages += "<tr><td><b>AsIs</b></td><td>0</td><td>Keep original encoding (code page)</td></tr>\n";
@@ -896,6 +898,7 @@ private void SendInternalPage(string InternalPageId, string Arguments)
896898
return;
897899
case "/!img-test":
898900
case "/!img-test/":
901+
// ImageMagick test v1
899902
if (ConfigFile.EnableManualConverting)
900903
{
901904
SendError(200, @"ImageMagick test.<br><img src=""/!convert/?src=logo.webp&dest=gif&type=image/gif"" alt=""ImageMagick logo"" width=640 height=480><br>A wizard should appear nearby.");
@@ -907,6 +910,7 @@ private void SendInternalPage(string InternalPageId, string Arguments)
907910
return;
908911
}
909912
case "/!imagemagicktest.gif":
913+
// ImageMagick test v2
910914
foreach (Converter Cvt in ConfigFile.Converters)
911915
{
912916
if (Cvt.Executable == "convert" && !Cvt.SelfDownload)
@@ -924,6 +928,7 @@ private void SendInternalPage(string InternalPageId, string Arguments)
924928
return;
925929
case "/!convert":
926930
case "/!convert/":
931+
// File format converting
927932
if (!ConfigFile.EnableManualConverting)
928933
{
929934
SendInfoPage("WebOne: Feature disabled", "Feature disabled", "Manual file converting is disabled for security purposes.<br>Proxy administrator can enable it via <code>[Server]</code> section, <code>EnableManualConverting</code> option.", 500);
@@ -1064,6 +1069,12 @@ private void SendInternalPage(string InternalPageId, string Arguments)
10641069
return;
10651070
case "/!webvideo":
10661071
case "/!webvideo/":
1072+
// ROVP video content processor
1073+
if (!ConfigFile.WebVideoOptions.ContainsKey("Enable") || !Program.ToBoolean(ConfigFile.WebVideoOptions["Enable"] ?? "yes"))
1074+
{
1075+
SendRedirect("/norovp.htm", "Video Converting and ROVP are disabled on this server.");
1076+
return;
1077+
}
10671078
Dictionary<string, string> VidArgs = new();
10681079

10691080
foreach (string UrlArg in System.Web.HttpUtility.ParseQueryString(ClientRequest.Url.Query).AllKeys)
@@ -1113,10 +1124,12 @@ private void SendInternalPage(string InternalPageId, string Arguments)
11131124
}
11141125
case "/!player":
11151126
case "/!player/":
1127+
// ROVP
11161128
SendInfoPage(new WebVideoPlayer(System.Web.HttpUtility.ParseQueryString(ClientRequest.Url.Query)).Page);
11171129
return;
11181130
case "/!clear":
11191131
case "/!clear/":
1132+
// Clear temporary files
11201133
int FilesDeleted = 0;
11211134
foreach (FileInfo file in (new DirectoryInfo(ConfigFile.TemporaryDirectory)).EnumerateFiles("convert-*.*"))
11221135
{
@@ -1127,14 +1140,15 @@ private void SendInternalPage(string InternalPageId, string Arguments)
11271140
return;
11281141
case "/!ftp":
11291142
case "/!ftp/":
1130-
//FTP client
1143+
// FTP client
11311144
SendInfoPage(new FtpClientGUI(ClientRequest).GetPage());
11321145
return;
11331146
case "/!ca":
11341147
case "/!ca/":
11351148
case "/weboneca.crt":
11361149
case "/weboneca.cer":
11371150
case "/weboneca.der":
1151+
// Root certificate (DER-encoded)
11381152
Log.WriteLine("<Return WebOne CA (root) certificate.");
11391153
if (!ConfigFile.SslEnable)
11401154
{
@@ -1161,6 +1175,7 @@ private void SendInternalPage(string InternalPageId, string Arguments)
11611175
return;
11621176
case "/weboneca.pem":
11631177
case "/weboneca.txt":
1178+
// Root certificate (PEM BASE64-encoded)
11641179
const string CRT_HEADER = "-----BEGIN CERTIFICATE-----\n";
11651180
const string CRT_FOOTER = "\n-----END CERTIFICATE-----";
11661181

@@ -1198,7 +1213,7 @@ private void SendInternalPage(string InternalPageId, string Arguments)
11981213
case "/auto.pac":
11991214
case "/wpad.dat":
12001215
case "/wpad.da":
1201-
//Proxy Auto-Config
1216+
// Proxy Auto-Config
12021217
Log.WriteLine("<Return PAC/WPAD script.");
12031218
string LocalHostAdress = GetServerName();
12041219
if (LocalHostAdress.StartsWith("[")) LocalHostAdress = ConfigFile.DefaultHostName + ":" + ConfigFile.Port; //on IPv6, fallback to DefaultHostName:Port
@@ -1223,7 +1238,7 @@ private void SendInternalPage(string InternalPageId, string Arguments)
12231238
}
12241239
return;
12251240
case "/robots.txt":
1226-
//attempt to include in google index; kick the bot off
1241+
// Attempt to include in Google index; kick the bot off
12271242
Log.WriteLine("<Return robot kicker.");
12281243
if (SendInternalContent("robots.txt", "")) return;
12291244

@@ -1246,11 +1261,14 @@ private void SendInternalPage(string InternalPageId, string Arguments)
12461261
}
12471262
return;
12481263
default:
1249-
if (InternalPageId.ToLowerInvariant() == "/rovp.htm" && !Program.ToBoolean(ConfigFile.WebVideoOptions["Enable"] ?? "yes"))
1264+
// Custom content (CSS or ROVP)
1265+
if (InternalPageId.ToLowerInvariant() == "/rovp.htm" &&
1266+
(!ConfigFile.WebVideoOptions.ContainsKey("Enable") || !Program.ToBoolean(ConfigFile.WebVideoOptions["Enable"] ?? "yes")))
12501267
{
12511268
SendRedirect("/norovp.htm", "ROVP is disabled on this server.");
12521269
return;
12531270
}
1271+
12541272
if (CheckInternalContentModification(InternalPageId, ClientRequest.Headers["If-Modified-Since"]))
12551273
{
12561274
// send 304 Not Modified code
@@ -1298,6 +1316,13 @@ public bool SendInternalContent(string ContentId, string Arguments, int StatusCo
12981316
{
12991317
if (!ContentId.StartsWith("!")) ContentId = "/" + ContentId;
13001318
string ContentFilePath = ConfigFile.ContentDirectory + ContentId;
1319+
1320+
ContentFilePath = ContentFilePath.Replace("//", "/").Replace(@"\\", @"\");
1321+
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
1322+
ContentFilePath = ContentFilePath.Replace("/", @"\");
1323+
if (Environment.OSVersion.Platform == PlatformID.Unix)
1324+
ContentFilePath = ContentFilePath.Replace(@"\", "/");
1325+
13011326
if (File.Exists(ContentFilePath))
13021327
{
13031328
string Extension = ContentId.Substring(ContentId.LastIndexOf('.') + 1).ToLowerInvariant();
@@ -1362,6 +1387,12 @@ public bool CheckInternalContentModification(string ContentId, string IfModified
13621387
if (!ContentId.StartsWith("!")) ContentId = "/" + ContentId;
13631388
string ContentFilePath = ConfigFile.ContentDirectory + ContentId;
13641389

1390+
ContentFilePath = ContentFilePath.Replace("//", "/").Replace(@"\\", @"\");
1391+
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
1392+
ContentFilePath = ContentFilePath.Replace("/", @"\");
1393+
if (Environment.OSVersion.Platform == PlatformID.Unix)
1394+
ContentFilePath = ContentFilePath.Replace(@"\", "/");
1395+
13651396
if (File.Exists(ContentFilePath))
13661397
{
13671398
var IMS = ToDateTimeOffset(IfModifiedSince);
@@ -1680,16 +1711,46 @@ private string ProcessBody(string Body)
16801711
//fix the body if it will be deliveried through Alternate mode
16811712
if (ClientRequest.Kind == HttpUtil.RequestKind.AlternateProxy || ClientRequest.Kind == HttpUtil.RequestKind.DirtyAlternateProxy)
16821713
{
1683-
Body = Body.Replace("http://", "http://" + GetServerName() + "/http://");
1684-
Body = Body.Replace("href=\"./", "href=\"http://" + GetServerName() + "/http://" + RequestURL.Host + "/");
1685-
Body = Body.Replace("src=\"./", "src=\"http://" + GetServerName() + "/http://" + RequestURL.Host + "/");
1686-
Body = Body.Replace("action=\"./", "action=\"http://" + GetServerName() + "/http://" + RequestURL.Host + "/");
1687-
Body = Body.Replace("href=\"//", "href=\"http://" + GetServerName() + "/http://");
1688-
Body = Body.Replace("src=\"//", "src=\"http://" + GetServerName() + "/http://");
1689-
Body = Body.Replace("action=\"//", "action=\"http://" + GetServerName() + "/http://");
1690-
Body = Body.Replace("href=\"/", "href=\"http://" + GetServerName() + "/http://" + RequestURL.Host + "/");
1691-
Body = Body.Replace("src=\"/", "src=\"http://" + GetServerName() + "/http://" + RequestURL.Host + "/");
1692-
Body = Body.Replace("action=\"/", "action=\"http://" + GetServerName() + "/http://" + RequestURL.Host + "/");
1714+
string LocalPathDirectory = "/";
1715+
for (int i = 0; i < RequestURL.Segments.Length - 1; i++)
1716+
{
1717+
LocalPathDirectory += RequestURL.Segments[i];
1718+
}
1719+
1720+
Body = Body.Replace(".replace(/\"/g", ".replace(/\"WEBONEGOOGLEFIX1/g");
1721+
Body = Body.Replace(".replace(/'/g", ".replace(/'WEBONEGOOGLEFIX2/g");
1722+
1723+
Body = Body.Replace("\"http://", "\"@@@/http://");
1724+
Body = Body.Replace("\"https://", "\"@@@/http://");
1725+
Body = Body.Replace("\"ftp://", "\"@@@/ftp://");
1726+
1727+
Body = Body.Replace("\"//", "\"@@@/http://");
1728+
Body = Body.Replace("\"/", "\"/http://" + RequestURL.Host + "/");
1729+
Body = Body.Replace("\"./", "\"/http://" + RequestURL.Host + LocalPathDirectory);
1730+
Body = Body.Replace("\"@@@/http://", "\"/http://");
1731+
Body = Body.Replace("\"@@@/ftp://", "\"/ftp://");
1732+
1733+
Body = Body.Replace("'http://", "'@@@/http://");
1734+
Body = Body.Replace("'https://", "'@@@/http://");
1735+
Body = Body.Replace("'ftp://", "'@@@/ftp://");
1736+
1737+
Body = Body.Replace("'//", "'@@@/http://");
1738+
Body = Body.Replace("'/", "'/http://" + RequestURL.Host + "/");
1739+
Body = Body.Replace("'./", "'/http://" + RequestURL.Host + LocalPathDirectory);
1740+
Body = Body.Replace("'@@@/http://", "'/http://");
1741+
Body = Body.Replace("'@@@/ftp://", "'/ftp://");
1742+
1743+
Body = Body.Replace(":url(http://", ":url(/http://" + RequestURL.Host + "/");
1744+
Body = Body.Replace(":url(https://", ":url(/http://" + RequestURL.Host + "/");
1745+
Body = Body.Replace(":url(//", ":url(@@@/http://");
1746+
Body = Body.Replace(":url(/", ":url(/http://" + RequestURL.Host + "/");
1747+
Body = Body.Replace(":url(./", ":url(/http://" + RequestURL.Host + LocalPathDirectory);
1748+
Body = Body.Replace(":url(@@@/http://", ":url(/http://");
1749+
1750+
Body = Body.Replace(".replace(/\"WEBONEGOOGLEFIX1/g", ".replace(/\"/g");
1751+
Body = Body.Replace(".replace(/'WEBONEGOOGLEFIX2/g", ".replace(/'/g");
1752+
1753+
// Note: "@@@" is workaround to differ strings.
16931754
}
16941755

16951756
return Body;
@@ -2060,12 +2121,17 @@ private bool LookInWebArchive()
20602121
}
20612122
catch (Exception ArchiveException)
20622123
{
2063-
string ErrorPageId = "Err-WArchiveException.htm";
2064-
string ErrorPageArguments = "?ErrorMessage=" + ArchiveException.Message.Replace("\n", "<br>") + "&URL=" + RequestURL.AbsoluteUri.ToString();
2065-
if (SendInternalContent(ErrorPageId, ErrorPageArguments)) return true;
2124+
try
2125+
{
2126+
string ErrorPageId = "Err-WArchiveException.htm";
2127+
string ErrorPageArguments = "?ErrorMessage=" + ArchiveException.Message.Replace("\n", "<br>") + "&URL=" + RequestURL.AbsoluteUri.ToString();
2128+
if (SendInternalContent(ErrorPageId, ErrorPageArguments)) return true;
20662129

2067-
SendInfoPage("WebOne: Web Archive error.", "Cannot load this page", string.Format("<b>The requested server or page is not found and a Web Archive error occured.</b><br>{0}", ArchiveException.Message.Replace("\n", "<br>")));
2068-
return true; //error page is ready
2130+
SendInfoPage("WebOne: Web Archive error.", "Cannot load this page", string.Format("<b>The requested server or page is not found and a Web Archive error occured.</b><br>{0}", ArchiveException.Message.Replace("\n", "<br>")));
2131+
return true; //error page is ready
2132+
}
2133+
catch (InvalidOperationException)
2134+
{ return true; } //catch case of "HTTP response headers are already sent."
20692135
}
20702136
}
20712137
else return false; //nothing ready

0 commit comments

Comments
 (0)