Commit f26c5605 authored by titanium007's avatar titanium007

issue #31, #32, #35

fix multiple issues reported
parent 37714ced
...@@ -14,22 +14,6 @@ namespace Titanium.Web.Proxy.Test ...@@ -14,22 +14,6 @@ namespace Titanium.Web.Proxy.Test
NativeMethods.SetConsoleCtrlHandler(NativeMethods.Handler, true); NativeMethods.SetConsoleCtrlHandler(NativeMethods.Handler, true);
Console.Write("Do you want to monitor HTTPS? (Y/N):");
var readLine = Console.ReadLine();
if (readLine != null && readLine.Trim().ToLower() == "y")
{
Controller.EnableSsl = true;
}
Console.Write("Do you want to set this as a System Proxy? (Y/N):");
var line = Console.ReadLine();
if (line != null && line.Trim().ToLower() == "y")
{
Controller.SetAsSystemProxy = true;
}
//Start proxy controller //Start proxy controller
Controller.StartProxy(); Controller.StartProxy();
......
using System; using System;
using System.Collections.Generic;
using System.Net; using System.Net;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
...@@ -8,29 +9,33 @@ namespace Titanium.Web.Proxy.Test ...@@ -8,29 +9,33 @@ namespace Titanium.Web.Proxy.Test
{ {
public class ProxyTestController public class ProxyTestController
{ {
public int ListeningPort { get; set; }
public bool EnableSsl { get; set; }
public bool SetAsSystemProxy { get; set; }
public void StartProxy() public void StartProxy()
{ {
ProxyServer.BeforeRequest += OnRequest; ProxyServer.BeforeRequest += OnRequest;
ProxyServer.BeforeResponse += OnResponse; ProxyServer.BeforeResponse += OnResponse;
//Exclude Https addresses you don't want to proxy //Exclude Https addresses you don't want to proxy
//Usefull for clients that use certificate pinning //Usefull for clients that use certificate pinning
//for example dropbox.com //for example dropbox.com
// ProxyServer.ExcludedHttpsHostNameRegex.Add(".dropbox.com"); var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Loopback, 8000, true){
var explicitEndPoint = new ExplicitProxyEndPoint { EnableSsl = true, IpAddress = IPAddress.Any, Port = 8000 }; ExcludedHostNameRegex = new List<string>() { "dropbox.com" }
var transparentEndPoint = new TransparentProxyEndPoint { EnableSsl = true, IpAddress = IPAddress.Loopback, Port = 443 }; };
var transparentEndPoint = new TransparentProxyEndPoint(IPAddress.Loopback, 8001, true)
{
};
ProxyServer.AddEndPoint(explicitEndPoint); ProxyServer.AddEndPoint(explicitEndPoint);
ProxyServer.AddEndPoint(transparentEndPoint); ProxyServer.AddEndPoint(transparentEndPoint);
ProxyServer.Start(); ProxyServer.Start();
ProxyServer.SetAsSystemProxy(explicitEndPoint);
foreach (var endPoint in ProxyServer.ProxyEndPoints)
Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ", endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
ProxyServer.SetAsSystemProxy(explicitEndPoint);
// Console.WriteLine("Proxy listening on local machine port: {0} ", ProxyServer.ListeningPort);
} }
public void Stop() public void Stop()
......
...@@ -8,8 +8,14 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -8,8 +8,14 @@ namespace Titanium.Web.Proxy.Extensions
{ {
public static Encoding GetResponseEncoding(this HttpWebSession response) public static Encoding GetResponseEncoding(this HttpWebSession response)
{ {
if (string.IsNullOrEmpty(response.Response.CharacterSet)) return Encoding.GetEncoding("ISO-8859-1"); if (string.IsNullOrEmpty(response.Response.CharacterSet))
return Encoding.GetEncoding(response.Response.CharacterSet.Replace(@"""", string.Empty)); return Encoding.GetEncoding("ISO-8859-1");
try
{
return Encoding.GetEncoding(response.Response.CharacterSet.Replace(@"""", string.Empty));
}
catch { return Encoding.GetEncoding("ISO-8859-1"); }
} }
} }
} }
\ No newline at end of file
...@@ -8,13 +8,17 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -8,13 +8,17 @@ namespace Titanium.Web.Proxy.Extensions
{ {
public static void CopyToAsync(this Stream input, string initialData, Stream output, int bufferSize) public static void CopyToAsync(this Stream input, string initialData, Stream output, int bufferSize)
{ {
var bytes = Encoding.ASCII.GetBytes(initialData); if(!string.IsNullOrEmpty(initialData))
output.Write(bytes, 0, bytes.Length); {
var bytes = Encoding.ASCII.GetBytes(initialData);
output.Write(bytes, 0, bytes.Length);
}
CopyToAsync(input, output, bufferSize); CopyToAsync(input, output, bufferSize);
} }
//http://stackoverflow.com/questions/1540658/net-asynchronous-stream-read-write //http://stackoverflow.com/questions/1540658/net-asynchronous-stream-read-write
public static void CopyToAsync(this Stream input, Stream output, int bufferSize) private static void CopyToAsync(this Stream input, Stream output, int bufferSize)
{ {
try try
{ {
......
...@@ -51,7 +51,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -51,7 +51,7 @@ namespace Titanium.Web.Proxy.Helpers
try try
{ {
sslStream = new SslStream(tunnelStream); sslStream = new SslStream(tunnelStream);
sslStream.AuthenticateAsClient(hostName); sslStream.AuthenticateAsClient(hostName, null, ProxyServer.SupportedProtocols, false);
tunnelStream = sslStream; tunnelStream = sslStream;
} }
catch catch
...@@ -69,10 +69,10 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -69,10 +69,10 @@ namespace Titanium.Web.Proxy.Helpers
if (sb != null) if (sb != null)
clientStream.CopyToAsync(sb.ToString(), tunnelStream, BUFFER_SIZE); clientStream.CopyToAsync(sb.ToString(), tunnelStream, BUFFER_SIZE);
else else
clientStream.CopyToAsync(tunnelStream, BUFFER_SIZE); clientStream.CopyToAsync(string.Empty, tunnelStream, BUFFER_SIZE);
}); });
var receiveRelay = Task.Factory.StartNew(() => tunnelStream.CopyToAsync(clientStream, BUFFER_SIZE)); var receiveRelay = Task.Factory.StartNew(() => tunnelStream.CopyToAsync(string.Empty, clientStream, BUFFER_SIZE));
Task.WaitAll(sendRelay, receiveRelay); Task.WaitAll(sendRelay, receiveRelay);
} }
......
...@@ -9,9 +9,16 @@ namespace Titanium.Web.Proxy.Models ...@@ -9,9 +9,16 @@ namespace Titanium.Web.Proxy.Models
{ {
public abstract class ProxyEndPoint public abstract class ProxyEndPoint
{ {
public IPAddress IpAddress { get; set; } public ProxyEndPoint(IPAddress IpAddress, int Port, bool EnableSsl)
public int Port { get; set; } {
public bool EnableSsl { get; set; } this.IpAddress = IpAddress;
this.Port = Port;
this.EnableSsl = EnableSsl;
}
public IPAddress IpAddress { get; internal set; }
public int Port { get; internal set; }
public bool EnableSsl { get; internal set; }
internal TcpListener listener { get; set; } internal TcpListener listener { get; set; }
} }
...@@ -20,11 +27,20 @@ namespace Titanium.Web.Proxy.Models ...@@ -20,11 +27,20 @@ namespace Titanium.Web.Proxy.Models
{ {
internal bool IsSystemProxy { get; set; } internal bool IsSystemProxy { get; set; }
public List<string> ExcludedHostNameRegex { get; set; } public List<string> ExcludedHostNameRegex { get; set; }
public ExplicitProxyEndPoint(IPAddress IpAddress, int Port, bool EnableSsl)
: base(IpAddress, Port, EnableSsl)
{
}
} }
public class TransparentProxyEndPoint : ProxyEndPoint public class TransparentProxyEndPoint : ProxyEndPoint
{ {
public TransparentProxyEndPoint(IPAddress IpAddress, int Port, bool EnableSsl)
: base(IpAddress, Port, EnableSsl)
{
}
} }
} }
...@@ -9,6 +9,7 @@ using System.IO; ...@@ -9,6 +9,7 @@ using System.IO;
using System.Net.Security; using System.Net.Security;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using System.Threading; using System.Threading;
using System.Security.Authentication;
namespace Titanium.Web.Proxy.Network namespace Titanium.Web.Proxy.Network
{ {
...@@ -76,7 +77,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -76,7 +77,7 @@ namespace Titanium.Web.Proxy.Network
try try
{ {
sslStream = new SslStream(stream); sslStream = new SslStream(stream);
sslStream.AuthenticateAsClient(Hostname); sslStream.AuthenticateAsClient(Hostname, null, ProxyServer.SupportedProtocols , false);
stream = (Stream)sslStream; stream = (Stream)sslStream;
} }
catch catch
......
...@@ -12,6 +12,7 @@ using Titanium.Web.Proxy.Helpers; ...@@ -12,6 +12,7 @@ using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network; using Titanium.Web.Proxy.Network;
using System.Linq; using System.Linq;
using System.Security.Authentication;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
...@@ -33,15 +34,18 @@ namespace Titanium.Web.Proxy ...@@ -33,15 +34,18 @@ namespace Titanium.Web.Proxy
private static readonly byte[] ChunkEnd = private static readonly byte[] ChunkEnd =
Encoding.ASCII.GetBytes(0.ToString("x2") + Environment.NewLine + Environment.NewLine); Encoding.ASCII.GetBytes(0.ToString("x2") + Environment.NewLine + Environment.NewLine);
private static List<ProxyEndPoint> _proxyEndPoints { get; set; } #if NET45
internal static SslProtocols SupportedProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Ssl3 ;
#else
public static SslProtocols SupportedProtocols = SslProtocols.Tls | SslProtocols.Ssl3;
#endif
static ProxyServer() static ProxyServer()
{ {
CertManager = new CertificateManager("Titanium", CertManager = new CertificateManager("Titanium",
"Titanium Root Certificate Authority"); "Titanium Root Certificate Authority");
_proxyEndPoints = new List<ProxyEndPoint>(); ProxyEndPoints = new List<ProxyEndPoint>();
Initialize(); Initialize();
} }
...@@ -56,6 +60,7 @@ namespace Titanium.Web.Proxy ...@@ -56,6 +60,7 @@ namespace Titanium.Web.Proxy
public static event EventHandler<SessionEventArgs> BeforeRequest; public static event EventHandler<SessionEventArgs> BeforeRequest;
public static event EventHandler<SessionEventArgs> BeforeResponse; public static event EventHandler<SessionEventArgs> BeforeResponse;
public static List<ProxyEndPoint> ProxyEndPoints { get; set; }
public static void Initialize() public static void Initialize()
{ {
...@@ -67,25 +72,25 @@ namespace Titanium.Web.Proxy ...@@ -67,25 +72,25 @@ namespace Titanium.Web.Proxy
if (proxyStarted) if (proxyStarted)
throw new Exception("Cannot add end points after proxy started."); throw new Exception("Cannot add end points after proxy started.");
_proxyEndPoints.Add(endPoint); ProxyEndPoints.Add(endPoint);
} }
public static void SetAsSystemProxy(ExplicitProxyEndPoint endPoint) public static void SetAsSystemProxy(ExplicitProxyEndPoint endPoint)
{ {
if (_proxyEndPoints.Contains(endPoint) == false) if (ProxyEndPoints.Contains(endPoint) == false)
throw new Exception("Cannot set endPoints not added to proxy as system proxy"); throw new Exception("Cannot set endPoints not added to proxy as system proxy");
if (!proxyStarted) if (!proxyStarted)
throw new Exception("Cannot set system proxy settings before proxy has been started."); throw new Exception("Cannot set system proxy settings before proxy has been started.");
//clear any settings previously added //clear any settings previously added
_proxyEndPoints.OfType<ExplicitProxyEndPoint>().ToList().ForEach(x => x.IsSystemProxy = false); ProxyEndPoints.OfType<ExplicitProxyEndPoint>().ToList().ForEach(x => x.IsSystemProxy = false);
endPoint.IsSystemProxy = true; endPoint.IsSystemProxy = true;
SystemProxyHelper.EnableProxyHttp( SystemProxyHelper.EnableProxyHttp(
Equals(endPoint.IpAddress, IPAddress.Any) ? "127.0.0.1" : endPoint.IpAddress.ToString(), endPoint.Port); Equals(endPoint.IpAddress, IPAddress.Any) | Equals(endPoint.IpAddress, IPAddress.Loopback) ? "127.0.0.1" : endPoint.IpAddress.ToString(), endPoint.Port);
#if !DEBUG #if !DEBUG
FireFoxHelper.AddFirefox(); FireFoxHelper.AddFirefox();
...@@ -99,21 +104,23 @@ namespace Titanium.Web.Proxy ...@@ -99,21 +104,23 @@ namespace Titanium.Web.Proxy
if (certTrusted) if (certTrusted)
{ {
SystemProxyHelper.EnableProxyHttps( SystemProxyHelper.EnableProxyHttps(
Equals(endPoint.IpAddress, IPAddress.Any) ? "127.0.0.1" : endPoint.IpAddress.ToString(), Equals(endPoint.IpAddress, IPAddress.Any) | Equals(endPoint.IpAddress, IPAddress.Loopback) ? "127.0.0.1" : endPoint.IpAddress.ToString(),
endPoint.Port); endPoint.Port);
} }
} }
Console.WriteLine("Set endpoint at Ip {1} and port: {2} as System Proxy", endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
} }
public static void Start() public static void Start()
{ {
EnableSsl = _proxyEndPoints.Any(x => x.EnableSsl); EnableSsl = ProxyEndPoints.Any(x => x.EnableSsl);
if (EnableSsl) if (EnableSsl)
certTrusted = CertManager.CreateTrustedRootCertificate(); certTrusted = CertManager.CreateTrustedRootCertificate();
foreach (var endPoint in _proxyEndPoints) foreach (var endPoint in ProxyEndPoints)
{ {
endPoint.listener = new TcpListener(endPoint.IpAddress, endPoint.Port); endPoint.listener = new TcpListener(endPoint.IpAddress, endPoint.Port);
...@@ -130,10 +137,7 @@ namespace Titanium.Web.Proxy ...@@ -130,10 +137,7 @@ namespace Titanium.Web.Proxy
private static void OnAcceptConnection(IAsyncResult asyn) private static void OnAcceptConnection(IAsyncResult asyn)
{ {
var endPoint = (ProxyEndPoint)asyn.AsyncState; var endPoint = (ProxyEndPoint)asyn.AsyncState;
// Get the listener that handles the client request.
endPoint.listener.BeginAcceptTcpClient(OnAcceptConnection, endPoint);
var client = endPoint.listener.EndAcceptTcpClient(asyn); var client = endPoint.listener.EndAcceptTcpClient(asyn);
try try
...@@ -147,12 +151,15 @@ namespace Titanium.Web.Proxy ...@@ -147,12 +151,15 @@ namespace Titanium.Web.Proxy
{ {
// ignored // ignored
} }
// Get the listener that handles the client request.
endPoint.listener.BeginAcceptTcpClient(OnAcceptConnection, endPoint);
} }
public static void Stop() public static void Stop()
{ {
var SetAsSystemProxy = _proxyEndPoints.OfType<ExplicitProxyEndPoint>().Any(x => x.IsSystemProxy); var SetAsSystemProxy = ProxyEndPoints.OfType<ExplicitProxyEndPoint>().Any(x => x.IsSystemProxy);
if (SetAsSystemProxy) if (SetAsSystemProxy)
{ {
...@@ -162,7 +169,7 @@ namespace Titanium.Web.Proxy ...@@ -162,7 +169,7 @@ namespace Titanium.Web.Proxy
#endif #endif
} }
foreach (var endPoint in _proxyEndPoints) foreach (var endPoint in ProxyEndPoints)
{ {
endPoint.listener.Stop(); endPoint.listener.Stop();
} }
......
...@@ -51,11 +51,10 @@ namespace Titanium.Web.Proxy ...@@ -51,11 +51,10 @@ namespace Titanium.Web.Proxy
var httpVersion = httpCmdSplit[2]; var httpVersion = httpCmdSplit[2];
var excluded = endPoint.ExcludedHostNameRegex != null ? endPoint.ExcludedHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x)) : false; var excluded = endPoint.ExcludedHostNameRegex != null ? endPoint.ExcludedHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x)) : false;
//Client wants to create a secure tcp tunnel (its a HTTPS request) //Client wants to create a secure tcp tunnel (its a HTTPS request)
if (httpVerb.ToUpper() == "CONNECT" && !excluded && httpRemoteUri.Port == 443) if (httpVerb.ToUpper() == "CONNECT" && !excluded && httpRemoteUri.Port!=80)
{ {
httpRemoteUri = new Uri("https://" + httpCmdSplit[1]); httpRemoteUri = new Uri("https://" + httpCmdSplit[1]);
clientStreamReader.ReadAllLines(); clientStreamReader.ReadAllLines();
...@@ -69,9 +68,10 @@ namespace Titanium.Web.Proxy ...@@ -69,9 +68,10 @@ namespace Titanium.Web.Proxy
try try
{ {
sslStream = new SslStream(clientStream, true); sslStream = new SslStream(clientStream, true);
//Successfully managed to authenticate the client using the fake certificate //Successfully managed to authenticate the client using the fake certificate
sslStream.AuthenticateAsServer(certificate, false, sslStream.AuthenticateAsServer(certificate, false,
SslProtocols.Tls | SslProtocols.Ssl3 | SslProtocols.Ssl2, false); SupportedProtocols, false);
clientStreamReader = new CustomBinaryReader(sslStream, Encoding.ASCII); clientStreamReader = new CustomBinaryReader(sslStream, Encoding.ASCII);
clientStreamWriter = new StreamWriter(sslStream); clientStreamWriter = new StreamWriter(sslStream);
...@@ -96,13 +96,16 @@ namespace Titanium.Web.Proxy ...@@ -96,13 +96,16 @@ namespace Titanium.Web.Proxy
{ {
clientStreamReader.ReadAllLines(); clientStreamReader.ReadAllLines();
WriteConnectResponse(clientStreamWriter, httpVersion); WriteConnectResponse(clientStreamWriter, httpVersion);
TcpHelper.SendRaw(clientStreamReader.BaseStream, null, null, httpRemoteUri.Host, httpRemoteUri.Port,
TcpHelper.SendRaw(clientStream, null, null, httpRemoteUri.Host, httpRemoteUri.Port,
false); false);
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
return; return;
} }
//Now create the request //Now create the request
HandleHttpSessionRequest(client, httpCmd, clientStream, clientStreamReader, clientStreamWriter, HandleHttpSessionRequest(client, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
httpRemoteUri.Scheme == Uri.UriSchemeHttps ? true : false); httpRemoteUri.Scheme == Uri.UriSchemeHttps ? true : false);
} }
...@@ -125,8 +128,8 @@ namespace Titanium.Web.Proxy ...@@ -125,8 +128,8 @@ namespace Titanium.Web.Proxy
sslStream.AuthenticateAsServer(certificate, false, sslStream.AuthenticateAsServer(certificate, false,
SslProtocols.Tls, false); SslProtocols.Tls, false);
clientStreamReader = new CustomBinaryReader(sslStream, Encoding.ASCII); clientStreamReader = new CustomBinaryReader(sslStream, Encoding.ASCII);
clientStreamWriter = new StreamWriter(sslStream); clientStreamWriter = new StreamWriter(sslStream);
//HTTPS server created - we can now decrypt the client's traffic //HTTPS server created - we can now decrypt the client's traffic
} }
...@@ -201,8 +204,8 @@ namespace Titanium.Web.Proxy ...@@ -201,8 +204,8 @@ namespace Titanium.Web.Proxy
if (args.ProxySession.Request.UpgradeToWebSocket) if (args.ProxySession.Request.UpgradeToWebSocket)
{ {
TcpHelper.SendRaw(clientStreamReader.BaseStream, httpCmd, args.ProxySession.Request.RequestHeaders, TcpHelper.SendRaw(clientStream, httpCmd, args.ProxySession.Request.RequestHeaders,
httpRemoteUri.Host, httpRemoteUri.Port, httpRemoteUri.Scheme == Uri.UriSchemeHttps); httpRemoteUri.Host, httpRemoteUri.Port, args.IsHttps);
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
return; return;
} }
......
...@@ -103,7 +103,7 @@ namespace Titanium.Web.Proxy ...@@ -103,7 +103,7 @@ namespace Titanium.Web.Proxy
if (response.Response.ResponseHeaders[i].Value.Contains(";")) if (response.Response.ResponseHeaders[i].Value.Contains(";"))
{ {
response.Response.ContentType = response.Response.ResponseHeaders[i].Value.Split(';')[0].Trim(); response.Response.ContentType = response.Response.ResponseHeaders[i].Value.Split(';')[0].Trim();
response.Response.CharacterSet = response.Response.ResponseHeaders[i].Value.Split(';')[1].ToLower().Replace("charset=", string.Empty).Trim(); response.Response.CharacterSet = response.Response.ResponseHeaders[i].Value.Split(';')[1].Substring(9).Trim();
} }
else else
response.Response.ContentType = response.Response.ResponseHeaders[i].Value.ToLower().Trim(); response.Response.ContentType = response.Response.ResponseHeaders[i].Value.ToLower().Trim();
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug-Net45|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug-Net45|AnyCPU'">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug-Net45\</OutputPath> <OutputPath>bin\Debug-Net45\</OutputPath>
<DefineConstants>DEBUG</DefineConstants> <DefineConstants>DEBUG;NET45</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<DefineConstants>NET45</DefineConstants>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Ionic.Zip, Version=1.9.7.0, Culture=neutral, PublicKeyToken=6583c7c814667745, processorArchitecture=MSIL"> <Reference Include="Ionic.Zip, Version=1.9.7.0, Culture=neutral, PublicKeyToken=6583c7c814667745, processorArchitecture=MSIL">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment