diff --git a/Teknik/Areas/API/Controllers/APIv1Controller.cs b/Teknik/Areas/API/Controllers/APIv1Controller.cs index 38c8e56..0ae75ec 100644 --- a/Teknik/Areas/API/Controllers/APIv1Controller.cs +++ b/Teknik/Areas/API/Controllers/APIv1Controller.cs @@ -31,7 +31,7 @@ namespace Teknik.Areas.API.Controllers { try { - Tracking.TrackPageView(Request, "Upload", Subdomain); + Tracking.TrackPageView(Request, "Upload"); if (file != null) { if (file.ContentLength <= Config.UploadConfig.MaxUploadSize) @@ -131,7 +131,7 @@ namespace Teknik.Areas.API.Controllers { try { - Tracking.TrackPageView(Request, "Paste", Subdomain); + Tracking.TrackPageView(Request, "Paste"); Paste.Models.Paste paste = PasteHelper.CreatePaste(code, title, syntax, expireUnit, expireLength, password, hide); db.Pastes.Add(paste); @@ -160,7 +160,7 @@ namespace Teknik.Areas.API.Controllers { try { - Tracking.TrackPageView(Request, "Shorten", Subdomain); + Tracking.TrackPageView(Request, "Shorten"); if (url.IsValidUrl()) { ShortenedUrl newUrl = Shortener.Shortener.ShortenUrl(url, Config.ShortenerConfig.UrlLength); diff --git a/Teknik/Areas/Upload/Controllers/UploadController.cs b/Teknik/Areas/Upload/Controllers/UploadController.cs index 45bfa75..707a4cb 100644 --- a/Teknik/Areas/Upload/Controllers/UploadController.cs +++ b/Teknik/Areas/Upload/Controllers/UploadController.cs @@ -147,7 +147,7 @@ namespace Teknik.Areas.Upload.Controllers Response.AppendHeader("Content-Disposition", cd.ToString()); // Handle Piwik Tracking if enabled - Tracking.TrackAction(Request.UserAgent, Request.Url.ToString()); + Tracking.TrackAction(Request, Request.Url.ToString()); return File(data, upload.ContentType); } diff --git a/Teknik/Configuration/PiwikConfig.cs b/Teknik/Configuration/PiwikConfig.cs index 5233924..ac0f796 100644 --- a/Teknik/Configuration/PiwikConfig.cs +++ b/Teknik/Configuration/PiwikConfig.cs @@ -14,11 +14,14 @@ namespace Teknik.Configuration public int SiteId { get; set; } + public string TokenAuth { get; set; } + public PiwikConfig() { Enabled = false; Url = string.Empty; SiteId = 1; + TokenAuth = string.Empty; } } } diff --git a/Teknik/Filters/TrackingFilterAttribute.cs b/Teknik/Filters/TrackingFilterAttribute.cs index 582d06d..d02ccec 100644 --- a/Teknik/Filters/TrackingFilterAttribute.cs +++ b/Teknik/Filters/TrackingFilterAttribute.cs @@ -20,30 +20,14 @@ namespace Teknik.Filters public override void OnActionExecuted(ActionExecutedContext filterContext) { - Config config = Config.Load(); - if (config.PiwikConfig.Enabled) - { - try - { - string sub = filterContext.HttpContext.Request.RequestContext.RouteData.Values["sub"].ToString(); - if (string.IsNullOrEmpty(sub)) - { - sub = filterContext.HttpContext.Request.Url.AbsoluteUri.GetSubdomain(); - } - string title = config.Title; - Page page = filterContext.HttpContext.Handler as Page; - - if (page != null) - { - title = page.Title; - } - Tracking.TrackPageView(filterContext.HttpContext.Request, title, sub); - } - catch (Exception ex) - { + string title = string.Empty; + Page page = filterContext.HttpContext.Handler as Page; - } + if (page != null) + { + title = page.Title; } + Tracking.TrackPageView(filterContext.HttpContext.Request, title); base.OnActionExecuted(filterContext); } diff --git a/Teknik/Global.asax.cs b/Teknik/Global.asax.cs index 03c5000..5686aa2 100644 --- a/Teknik/Global.asax.cs +++ b/Teknik/Global.asax.cs @@ -46,40 +46,22 @@ namespace Teknik var stopwatch = new Stopwatch(); HttpContext.Current.Items["Stopwatch"] = stopwatch; stopwatch.Start(); - - // Handle Piwik Tracking if enabled - Config config = Config.Load(); - if (config.PiwikConfig.Enabled) - { - try - { - HttpRequest request = base.Request; - - string sub = request.RequestContext.RouteData.Values["sub"].ToString(); - if (string.IsNullOrEmpty(sub)) - { - sub = request.Url.AbsoluteUri.GetSubdomain(); - } - string title = config.Title; - Page page = HttpContext.Current.Handler as Page; - - if (page != null) - { - title = page.Title; - } - Tracking.TrackPageView(new HttpRequestWrapper(request), title, sub); - } - catch (Exception ex) - { - - } - } } protected void Application_EndRequest(object sender, EventArgs e) { HttpContext context = HttpContext.Current; + // Handle Piwik + string title = string.Empty; + Page page = HttpContext.Current.Handler as Page; + + if (page != null) + { + title = page.Title; + } + Tracking.TrackPageView(new HttpRequestWrapper(context.Request), title); + Stopwatch stopwatch = (Stopwatch)context.Items["Stopwatch"]; stopwatch.Stop(); diff --git a/Teknik/Helpers/Crypto.cs b/Teknik/Helpers/Crypto.cs index 80cabb1..5d13910 100644 --- a/Teknik/Helpers/Crypto.cs +++ b/Teknik/Helpers/Crypto.cs @@ -15,6 +15,29 @@ using System; namespace Teknik.Helpers { + public class MD5 + { + public static string Hash(string value) + { + byte[] valBytes = Encoding.ASCII.GetBytes(value); + System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create(); + byte[] hashBytes = md5.ComputeHash(valBytes); + + StringBuilder sBuilder = new StringBuilder(); + + // Loop through each byte of the hashed data + // and format each one as a hexadecimal string. + for (int i = 0; i < hashBytes.Length; i++) + { + sBuilder.Append(hashBytes[i].ToString("x2")); + } + + // Return the hexadecimal string. + return sBuilder.ToString(); + + } + } + public class SHA384 { public static string Hash(string key, string value) diff --git a/Teknik/Helpers/RequestHelper.cs b/Teknik/Helpers/RequestHelper.cs new file mode 100644 index 0000000..6942ded --- /dev/null +++ b/Teknik/Helpers/RequestHelper.cs @@ -0,0 +1,231 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; +using System.Web; + +namespace Teknik.Helpers +{ + public static class RequestHelper + { + /// + /// method to get Client ip address + /// + /// set to true if want to get local(LAN) Connected ip address + /// + public static string GetVisitorIPAddress(bool GetLan = false) + { + string visitorIPAddress = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"]; + + if (String.IsNullOrEmpty(visitorIPAddress)) + visitorIPAddress = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"]; + + if (string.IsNullOrEmpty(visitorIPAddress)) + visitorIPAddress = HttpContext.Current.Request.UserHostAddress; + + if (string.IsNullOrEmpty(visitorIPAddress) || visitorIPAddress.Trim() == "::1") + { + GetLan = true; + visitorIPAddress = string.Empty; + } + + if (GetLan) + { + if (string.IsNullOrEmpty(visitorIPAddress)) + { + //This is for Local(LAN) Connected ID Address + string stringHostName = Dns.GetHostName(); + //Get Ip Host Entry + IPHostEntry ipHostEntries = Dns.GetHostEntry(stringHostName); + //Get Ip Address From The Ip Host Entry Address List + IPAddress[] arrIpAddress = ipHostEntries.AddressList; + + try + { + visitorIPAddress = arrIpAddress[arrIpAddress.Length - 2].ToString(); + } + catch + { + try + { + visitorIPAddress = arrIpAddress[0].ToString(); + } + catch + { + try + { + arrIpAddress = Dns.GetHostAddresses(stringHostName); + visitorIPAddress = arrIpAddress[0].ToString(); + } + catch + { + visitorIPAddress = "127.0.0.1"; + } + } + } + } + } + return visitorIPAddress; + } + + // based on http://www.grantburton.com/2008/11/30/fix-for-incorrect-ip-addresses-in-wordpress-comments/ + public static string ClientIPFromRequest(this HttpRequestBase request, bool skipPrivate) + { + foreach (var item in s_HeaderItems) + { + var ipString = request.Headers[item.Key]; + + if (String.IsNullOrEmpty(ipString)) + continue; + + if (item.Split) + { + foreach (var ip in ipString.Split(',')) + if (ValidIP(ip, skipPrivate)) + return ip; + } + else + { + if (ValidIP(ipString, skipPrivate)) + return ipString; + } + } + + return request.UserHostAddress; + } + + public static string DumpHeaders(this HttpRequestBase request) + { + var headers = string.Empty; + foreach (var key in request.Headers.AllKeys) + headers += key + "=" + request.Headers[key] + Environment.NewLine; + return headers; + } + + public static string DumpServerVariables(this HttpRequestBase request) + { + var variables = string.Empty; + foreach (var key in request.ServerVariables.AllKeys) + variables += key + "=" + request.ServerVariables[key] + Environment.NewLine; + return variables; + } + + private static bool ValidIP(string ip, bool skipPrivate) + { + IPAddress ipAddr; + + ip = ip == null ? String.Empty : ip.Trim(); + + if (0 == ip.Length + || false == IPAddress.TryParse(ip, out ipAddr) + || (ipAddr.AddressFamily != AddressFamily.InterNetwork + && ipAddr.AddressFamily != AddressFamily.InterNetworkV6)) + return false; + + if (skipPrivate && ipAddr.AddressFamily == AddressFamily.InterNetwork) + { + var addr = IpRange.AddrToUInt64(ipAddr); + foreach (var range in s_PrivateRanges) + { + if (range.Encompasses(addr)) + return false; + } + } + + return true; + } + + /// + /// Provides a simple class that understands how to parse and + /// compare IP addresses (IPV4) ranges. + /// + private sealed class IpRange + { + private readonly UInt64 _start; + private readonly UInt64 _end; + + public IpRange(string startStr, string endStr) + { + _start = ParseToUInt64(startStr); + _end = ParseToUInt64(endStr); + } + + public static UInt64 AddrToUInt64(IPAddress ip) + { + var ipBytes = ip.GetAddressBytes(); + UInt64 value = 0; + + foreach (var abyte in ipBytes) + { + value <<= 8; // shift + value += abyte; + } + + return value; + } + + public static UInt64 ParseToUInt64(string ipStr) + { + var ip = IPAddress.Parse(ipStr); + return AddrToUInt64(ip); + } + + public bool Encompasses(UInt64 addrValue) + { + return _start <= addrValue && addrValue <= _end; + } + + public bool Encompasses(IPAddress addr) + { + var value = AddrToUInt64(addr); + return Encompasses(value); + } + }; + + private static readonly IpRange[] s_PrivateRanges = + new IpRange[] { + new IpRange("0.0.0.0","2.255.255.255"), + new IpRange("10.0.0.0","10.255.255.255"), + new IpRange("127.0.0.0","127.255.255.255"), + new IpRange("169.254.0.0","169.254.255.255"), + new IpRange("172.16.0.0","172.31.255.255"), + new IpRange("192.0.2.0","192.0.2.255"), + new IpRange("192.168.0.0","192.168.255.255"), + new IpRange("255.255.255.0","255.255.255.255") + }; + + + /// + /// Describes a header item (key) and if it is expected to be + /// a comma-delimited string + /// + private sealed class HeaderItem + { + public readonly string Key; + public readonly bool Split; + + public HeaderItem(string key, bool split) + { + Key = key; + Split = split; + } + } + + // order is in trust/use order top to bottom + private static readonly HeaderItem[] s_HeaderItems = + new HeaderItem[] { + new HeaderItem("HTTP_CLIENT_IP",false), + new HeaderItem("HTTP_X_FORWARDED_FOR",true), + new HeaderItem("HTTP_X_FORWARDED",false), + new HeaderItem("HTTP_X_CLUSTER_CLIENT_IP",false), + new HeaderItem("HTTP_FORWARDED_FOR",false), + new HeaderItem("HTTP_FORWARDED",false), + new HeaderItem("HTTP_VIA",false), + new HeaderItem("REMOTE_ADDR",false) + }; + + } +} diff --git a/Teknik/Helpers/Tracking.cs b/Teknik/Helpers/Tracking.cs index 4bf1b3c..0e8c21a 100644 --- a/Teknik/Helpers/Tracking.cs +++ b/Teknik/Helpers/Tracking.cs @@ -11,7 +11,7 @@ namespace Teknik.Helpers { public static class Tracking { - public static void TrackPageView(HttpRequestBase request, string title, string sub) + public static void TrackPageView(HttpRequestBase request, string title) { Config config = Config.Load(); // Handle Piwik Tracking if enabled @@ -19,22 +19,35 @@ namespace Teknik.Helpers { try { + string sub = request.RequestContext.RouteData.Values["sub"].ToString(); + if (string.IsNullOrEmpty(sub)) + { + sub = request.Url.AbsoluteUri.GetSubdomain(); + } + + if (string.IsNullOrEmpty(title)) + { + title = config.Title; + } + PiwikTracker.URL = config.PiwikConfig.Url; PiwikTracker tracker = new PiwikTracker(config.PiwikConfig.SiteId); - - tracker.setForceVisitDateTime(DateTime.Now); + tracker.setUserAgent(request.UserAgent); tracker.setResolution(request.Browser.ScreenPixelsWidth, request.Browser.ScreenPixelsHeight); tracker.setBrowserHasCookies(request.Browser.Cookies); - string ipAddress = request.UserHostAddress; + string ipAddress = request.ClientIPFromRequest(true); tracker.setIp(ipAddress); + tracker.setTokenAuth(config.PiwikConfig.TokenAuth); tracker.setUrl(request.Url.ToString()); - tracker.setUrlReferrer(request.UrlReferrer.ToString()); + if (request.UrlReferrer != null) + tracker.setUrlReferrer(request.UrlReferrer.ToString()); + tracker.setRequestTimeout(5); tracker.doTrackPageView(string.Format("{0} / {1}", sub, title)); } catch (Exception ex) @@ -44,7 +57,7 @@ namespace Teknik.Helpers } } - public static void TrackAction(string userAgent, string url) + public static void TrackAction(HttpRequestBase request, string url) { Config config = Config.Load(); // Handle Piwik Tracking if enabled @@ -55,7 +68,12 @@ namespace Teknik.Helpers PiwikTracker.URL = config.PiwikConfig.Url; PiwikTracker tracker = new PiwikTracker(config.PiwikConfig.SiteId); - tracker.setUserAgent(userAgent); + tracker.setUserAgent(request.UserAgent); + + string ipAddress = request.ClientIPFromRequest(true); + + tracker.setIp(ipAddress); + tracker.setTokenAuth(config.PiwikConfig.TokenAuth); tracker.doTrackAction(url, PiwikTracker.ActionType.download); } diff --git a/Teknik/Modules/PerformanceMonitorModule.cs b/Teknik/Modules/PerformanceMonitorModule.cs index 85d0813..3e483d5 100644 --- a/Teknik/Modules/PerformanceMonitorModule.cs +++ b/Teknik/Modules/PerformanceMonitorModule.cs @@ -30,8 +30,6 @@ namespace Teknik.Modules if (requestContext.Response.ContentType == "text/html" && requestContext.Response.StatusCode == 200) { - - Uri requestUrl = requestContext.Request.Url; double ms = (double)timer.ElapsedMilliseconds; string result = string.Format("{0:F0}", ms); diff --git a/Teknik/Teknik.csproj b/Teknik/Teknik.csproj index 0303e72..39c0be8 100644 --- a/Teknik/Teknik.csproj +++ b/Teknik/Teknik.csproj @@ -266,6 +266,7 @@ + diff --git a/Teknik/Views/Shared/_Layout.cshtml b/Teknik/Views/Shared/_Layout.cshtml index 6f92eef..a2b7a02 100644 --- a/Teknik/Views/Shared/_Layout.cshtml +++ b/Teknik/Views/Shared/_Layout.cshtml @@ -30,6 +30,12 @@ + @if (Model.Config.PiwikConfig.Enabled) + { + + + + } @@ -45,6 +51,27 @@ @RenderBody() @Html.Partial("_Footer") + + @if (Model.Config.PiwikConfig.Enabled) + { + + + + }