/// /// Copyright © 2003-2008 JetBrains s.r.o. /// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder). /// using System; using System.Net; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Xml; namespace JetBrains.ExceptionReport { public struct RPCSubmissionResult { public int ThreadID; public bool IsUpdated; public string RequestDescription; } public class SubmissionResult { private int myThreadID; private bool myIsUpdated; private XmlDocument myRequestDescription; public SubmissionResult(int threadId, bool isUpdated, XmlDocument requestDescription) { myThreadID = threadId; myIsUpdated = isUpdated; myRequestDescription = requestDescription; } public int ThreadId { get { return myThreadID; } } public bool IsUpdated { get { return myIsUpdated; } } public XmlDocument RequestDescription { get { return myRequestDescription; } } } public interface IExceptionSubmitter { event SubmitProgressEventHandler SubmitProgress; SubmissionResult SubmitException(Exception e, string description, string userName, string password, int buildNumber, IWebProxy proxy); } /// /// Utility class for submitting exceptions to the ITN tracker. /// public class ITNExceptionSubmitter: IExceptionSubmitter { private readonly string myITNProjectName; private readonly string myProduct; private bool myNeedProcessEvents; public ITNExceptionSubmitter(string itnProjectName, string product) { myITNProjectName = itnProjectName; myProduct = product; myNeedProcessEvents = true; } public bool NeedProcessEvents { set { myNeedProcessEvents = value; } } public event SubmitProgressEventHandler SubmitProgress; protected void OnSubmitProgress(string message) { if (SubmitProgress != null) SubmitProgress(this, new SubmitProgressEventArgs(message)); } public SubmissionResult SubmitException(Exception e, string description, string userName, string password, int buildNumber, IWebProxy proxy) { return SubmitException( e.ToString(), e.Message, e.StackTrace, description, userName, password, buildNumber, null, proxy ); } public SubmissionResult SubmitException(string excString, string excMessage, string excStackTrace, string description, string itnUserName, string itnPassword, int buildNumber, string osVersion, IWebProxy proxy) { string md5Hash = GetExceptionHash( excString ); ErrorReportProxy reportProxy = new ErrorReportProxy( proxy, myNeedProcessEvents ); XmlDocument requestDescription = null; try { OnSubmitProgress("Checking..."); int itnThread = -1; ExceptionStruct es = new ExceptionStruct(); bool checkFailed = reportProxy.CheckException( md5Hash, ref es ); if ( !checkFailed ) { itnThread = es.exceptionItnThread; } OnSubmitProgress("Posting..."); bool isComment = true; string errDescription = (description.Length == 0) ? excString : description + "\n" + excString; if (itnThread == -1) { isComment = false; itnThread = ITNProxy.PostNewThread(myProduct, GetExceptionTitle(excMessage, excStackTrace), errDescription, itnUserName, itnPassword, buildNumber, osVersion); } else { ITNProxy.PostNewComment(myProduct, errDescription, itnUserName, itnPassword, itnThread); } if (myITNProjectName != String.Empty) { requestDescription = ITNProxy.GetSCR(myITNProjectName, itnThread); } if (!checkFailed) { if (!isComment) { es = new ExceptionStruct(); es.exceptionHash = md5Hash; es.exceptionMessage = excMessage; es.exceptionDate = DateTime.Now; es.exceptionStack = excStackTrace == null ? String.Empty : excStackTrace; es.exceptionItnThread = itnThread; es.exceptionBuildNumber = buildNumber.ToString(); es.exceptionProductCode = myProduct; es.exceptionScrambled = false; } OnSubmitProgress("Submitting..."); reportProxy.SubmitException( itnUserName, es, excString, isComment ); return new SubmissionResult(itnThread, isComment, requestDescription); } else return null; } finally { OnSubmitProgress(""); } } internal static string FilterExceptionString(string s) { Regex rx = new Regex(@"([^\s]+\\)?([^\s\\]+)(:.+)?$", RegexOptions.Multiline); return rx.Replace(s, "$2").ToLower(); } internal static string GetExceptionHash( string excString ) { string filteredString = FilterExceptionString(excString); byte[] excBytes = Encoding.Default.GetBytes(filteredString); return Convert.ToBase64String(new MD5CryptoServiceProvider().ComputeHash(excBytes)); } internal static string GetExceptionTitle(string excMessage, string excStackTrace) { string msg = excMessage; string[] stackTrace = (excStackTrace == null) ? new string[0] : excStackTrace.Split('\n'); foreach (string line in stackTrace) { int pos = line.IndexOf('('); if (pos >= 0) { int methodNameStart = line.LastIndexOf(' ', pos); if (methodNameStart < 0) methodNameStart = 0; if (line.Substring(methodNameStart + 1).StartsWith("System.")) continue; int lastDot = line.LastIndexOf('.', pos); // start of method name if (lastDot >= 0) // start of class name lastDot = line.LastIndexOf('.', lastDot - 1); if (lastDot >= 0) { msg = msg + " (" + line.Substring(lastDot + 1, pos - lastDot - 1) + ")"; break; } } } return msg; } } public class SubmitProgressEventArgs : EventArgs { private string _message; public SubmitProgressEventArgs(string message) { _message = message; } public string Message { get { return _message; } } } public delegate void SubmitProgressEventHandler(object sender, SubmitProgressEventArgs e); }