public static string ExplainOrigin(this Revision revision, User user)
        {
            string relationship = "initial version";
            int userId = user.IsAnonymous ? 0 : user.Id;

            if (revision.Parent != null)
            {
                if (revision.Parent.OwnerId != userId)
                {
                    relationship = "forked from";

                    if (revision.Parent != null)
                    {
                        relationship += " " + revision.Parent.Owner.Login + "'" + (!revision.Parent.Owner.Login.EndsWith("s") ? "s" : "");
                    }
                }
                else
                {
                    relationship = "created from your";
                }

                relationship += " revision " + revision.Parent.Id;
            }

            return "revision " + revision.Id + ", " + relationship;
        }
        public static void EnsureSafe(NameValueCollection form, User CurrentUser)
        {
            string xsrfFormValue = form["fkey"];

            if (xsrfFormValue.IsNullOrEmpty())
                throw new InvalidOperationException("XSRF validation: Request did not have required form value 'fkey'");

            if (!xsrfFormValue.Equals(CurrentUser.XSRFFormValue, StringComparison.OrdinalIgnoreCase))
                throw new InvalidOperationException(
                    "XSRF validation: Request form value 'fkey' did not match CurrentUser.XSRFFormValue");

            Debug.WriteLine("XSRFSafeAttribute.EnsureSafe => true");
        }
        public ActionResult Edit(int id, User updatedUser)
        {
            User user = Current.DB.Users.Get(id);

            if (updatedUser.DOB < DateTime.UtcNow.AddYears(-100) || updatedUser.DOB > DateTime.UtcNow.AddYears(-6))
            {
                updatedUser.DOB = null;
            }

            if (user.Id == updatedUser.Id && (updatedUser.Id == CurrentUser.Id || CurrentUser.IsAdmin))
            {
                var violations = updatedUser.GetBusinessRuleViolations(ChangeAction.Update);

                if (violations.Count == 0)
                {
                    var snapshot = Snapshotter.Start(user);
                    user.Login = HtmlUtilities.Safe(updatedUser.Login);
                    user.AboutMe = updatedUser.AboutMe;
                    user.DOB = updatedUser.DOB;
                    user.Email = HtmlUtilities.Safe(updatedUser.Email);
                    user.Website = HtmlUtilities.Safe(updatedUser.Website);
                    user.Location = HtmlUtilities.Safe(updatedUser.Location);

                    // Preferences are updated separately, so we should likely do this elsewhere instead...
                    // Can likely move it out if we have introduce a fancier OpenID management panel like
                    // the network has.
                    user.EnforceSecureOpenId = updatedUser.EnforceSecureOpenId;

                    var diff = snapshot.Diff();

                    if (diff.ParameterNames.Any())
                    {
                        Current.DB.Users.Update(user.Id, snapshot.Diff());
                    }

                    return Redirect("/users/" + user.Id);
                }
                else
                {
                    foreach (var violation in violations)
                        ModelState.AddModelError(violation.PropertyName, violation.ErrorMessage);

                    return Edit(user.Id);
                }
            }
            else
            {
                return Redirect("/");
            }
        }
        public ActionResult Edit(int id, User updatedUser)
        {
            User user = Current.DB.Users.First(u => u.Id == id);

            if (updatedUser.DOB < DateTime.Now.AddYears(-100) || updatedUser.DOB > DateTime.Now.AddYears(-6))
            {
                updatedUser.DOB = null;
            }

            if (user.Id == updatedUser.Id && (updatedUser.Id == CurrentUser.Id || CurrentUser.IsAdmin))
            {
                var violations = updatedUser.GetBusinessRuleViolations(ChangeAction.Update);

                if (violations.Count == 0)
                {
                    user.Login = HtmlUtilities.Safe(updatedUser.Login);
                    user.AboutMe = updatedUser.AboutMe;
                    user.DOB = updatedUser.DOB;
                    user.Email = HtmlUtilities.Safe(updatedUser.Email);
                    user.Website = HtmlUtilities.Safe(updatedUser.Website);
                    user.Location = HtmlUtilities.Safe(updatedUser.Location);

                    Current.DB.SubmitChanges();

                    return Redirect("/users/" + user.Id);
                }
                else
                {
                    foreach (var violation in violations)
                        ModelState.AddModelError(violation.PropertyName, violation.ErrorMessage);

                    return Edit(user.Id);
                }
            }
            else
            {
                return Redirect("/");
            }
        }
        public static QueryResults ExecuteNonCached(ParsedQuery query, Site site, User user)
        {
            var remoteIP = OData.GetRemoteIP();
            var key = "total-" + remoteIP;
            var currentCount = (int?)Current.GetCachedObject(key) ?? 0;
            currentCount++;
            Current.SetCachedObjectSliding(key, currentCount, 60 * 60);

            if (currentCount > 130)
            {
                // clearly a robot, auto black list
                var b = new BlackList { CreationDate = DateTime.UtcNow, IPAddress = remoteIP };
            }

            if (currentCount > 100)
            {
                throw new Exception("You can not run any new queries for another hour, you have exceeded your limit!");
            }

            if (Current.DB.ExecuteQuery<int>("select count(*) from BlackList where IPAddress = {0}", remoteIP).First() > 0)
            {
                System.Threading.Thread.Sleep(2000);
                throw new Exception("You have been blacklisted due to abuse!");
            }

            var results = new QueryResults();

            using (SqlConnection cnn = site.GetConnection())
            {
                cnn.Open();

                // well we do not want to risk blocking, if somebody needs to change this we will need to add a setting
                cnn.Execute("set transaction isolation level read uncommitted");

                var timer = new Stopwatch();
                timer.Start();

                var messages = new StringBuilder();

                var infoHandler = new SqlInfoMessageEventHandler((sender, args) =>
                                                                     {
                                                                         // todo handle errors as well
                                                                         messages.AppendLine(args.Message);
                                                                     });

                try
                {
                    cnn.InfoMessage += infoHandler;

                    if (query.IncludeExecutionPlan)
                    {
                        using (var command = new SqlCommand("SET STATISTICS XML ON", cnn))
                        {
                            command.ExecuteNonQuery();
                        }
                    }

                    var plan = new QueryPlan();

                    foreach (string batch in query.ExecutionSqlBatches)
                    {
                        using (var command = new SqlCommand(batch, cnn))
                        {
                            command.CommandTimeout = QUERY_TIMEOUT;
                            PopulateResults(results, command, messages, query.IncludeExecutionPlan);
                        }

                        if (query.IncludeExecutionPlan)
                        {
                            plan.AppendBatchPlan(results.ExecutionPlan);
                            results.ExecutionPlan = null;
                        }
                    }

                    results.ExecutionPlan = plan.PlanXml;
                }
                finally
                {
                    cnn.InfoMessage -= infoHandler;
                    results.Messages = messages.ToString();
                }

                timer.Stop();
                results.ExecutionTime = timer.ElapsedMilliseconds;

                ProcessMagicColumns(results, cnn);
            }

            return results;
        }
        public static User CreateUser(string accountLogin)
        {
            var u = new User
            {
                CreationDate = DateTime.UtcNow,
                Login = accountLogin,
                ADLogin = accountLogin,
            };
            u.Id = Current.DB.Users.Insert(new { u.Login, u.ADLogin, u.CreationDate }).Value;

            return u;
        }
        private static QueryResults GetSingleSiteResults(ParsedQuery query, Site site, User user, AsyncQueryRunner.AsyncResult result = null)
        {
            QueryResults results = null;
            var timer = new Stopwatch();

            timer.Start();

            var cache = QueryUtil.GetCachedResults(query, site.Id);

            if (cache != null)
            {
                if (!query.IncludeExecutionPlan || cache.ExecutionPlan != null)
                {
                    results = new QueryResults();
                    results.WithCache(cache);
                    results.Truncated = cache.Truncated;
                    results.Messages = cache.Messages;
                    results.FromCache = true;

                    // If we didn't ask for the execution plan, don't return it
                    if (!query.IncludeExecutionPlan)
                    {
                        results.ExecutionPlan = null;
                    }
                }
            }

            timer.Stop();

            if (results == null)
            {
                results = ExecuteNonCached(query, site, user, result);
                results.FromCache = false;

                // Don't cache cancelled results, since we don't know what state they're in...
                if (result != null && !result.Cancelled)
                {
                    AddResultToCache(results, query, site, cache != null);
                }
            }
            else
            {
                results.ExecutionTime = timer.ElapsedMilliseconds;
            }

            results.Url = site.Url;
            results.SiteId = site.Id;
            results.SiteName = site.Name.ToLower();

            return results;
        }
        public static QueryResults ExecuteNonCached(ParsedQuery query, Site site, User user, AsyncQueryRunner.AsyncResult result)
        {
            var remoteIP = OData.GetRemoteIP(); 
            var key = "total-" + remoteIP;
            var currentCount = (int?)Current.GetCachedObject(key) ?? 0;
            currentCount++;
            Current.SetCachedObjectSliding(key, currentCount, 60 * 60);

            if (currentCount > 130)
            {
                // clearly a robot, auto black list 
                Current.DB.BlackList.Insert(new { CreationDate = DateTime.UtcNow, IPAddress = remoteIP });
            }

            if (currentCount > 100)
            {
                throw new Exception("You can not run any new queries for another hour, you have exceeded your limit!");
            }

            if (Current.DB.Query<int>("select count(*) from BlackList where IPAddress = @remoteIP", new { remoteIP }).First() > 0)
            {
                System.Threading.Thread.Sleep(2000);
                throw new Exception("You have been blacklisted due to abuse!");
            }

            var results = new QueryResults();

            using (SqlConnection cnn = site.GetOpenConnection())
            {
                // well we do not want to risk blocking, if somebody needs to change this we will need to add a setting
                cnn.Execute("set transaction isolation level read uncommitted");

                var timer = new Stopwatch();
                timer.Start();

                var messages = new StringBuilder();

                var infoHandler = new SqlInfoMessageEventHandler((sender, args) =>
                                                                     {
                                                                         // todo handle errors as well
                                                                         messages.AppendLine(args.Message);
                                                                     });
                try
                {
                    cnn.InfoMessage += infoHandler;

                    if (query.IncludeExecutionPlan)
                    {
                        using (var command = new SqlCommand("SET STATISTICS XML ON", cnn))
                        {
                            command.ExecuteNonQuery();
                        }
                    }

                    var plan = new QueryPlan();

                    foreach (string batch in query.ExecutionSqlBatches)
                    {
                        using (var command = new SqlCommand(batch, cnn))
                        {
                            if (result != null)
                            {
                                result.Command = command;
                                if (result.Cancelled)
                                {
                                    continue;
                                }
                            }
                            command.CommandTimeout = AppSettings.QueryTimeout;

                            try
                            {
                                PopulateResults(results, command, result, messages, query.IncludeExecutionPlan);
                            }
                            catch (Exception ex)
                            {
                                // Ugh. So, if we cancel the query in-process, we get an exception...
                                // But we have no good way of knowing that the exception here is actually
                                // *that* exception...so we'll just assume it was if the state is Cancelled
                                if (result == null || result.State != AsyncQueryRunner.AsyncState.Cancelled)
                                {
                                    throw ex;
                                }
                            }
                        }

                        if (query.IncludeExecutionPlan)
                        {
                            plan.AppendBatchPlan(results.ExecutionPlan);
                            results.ExecutionPlan = null;
                        }
                    }

                    results.ExecutionPlan = plan.PlanXml;
                }
                finally
                {
                    cnn.InfoMessage -= infoHandler;
                    results.Messages = messages.ToString();
                }

                timer.Stop();
                results.ExecutionTime = timer.ElapsedMilliseconds;

                ProcessMagicColumns(results, cnn);
            }

            return results;
        }
Example #9
0
        private int? FindUserId(User user)
        {
            if (!user.IsAnonymous && user.Email != null)
            {

                using (var cnn = GetOpenConnection())
                {
                    string hash = Util.GravatarHash(user.Email);
                    try
                    {
                        return cnn.Query<int?>("select top 1 Id from Users where EmailHash = @hash order by Reputation desc", new {hash}).FirstOrDefault();
                    }
                    catch
                    {
                        // allow this to fail, its not critical
                    }
                }
            }
            return null;
        }
Example #10
0
        public static User CreateUser(string login, string email, string openIdClaim)
        {
            var u = new User();
            u.CreationDate = DateTime.UtcNow;

            login = CleanLogin(login ?? string.Empty);

            if (login.Length == 0)
            {
                login = emptyLogin;
            }

            u.Login = login;
            u.Email = email;

            int retries = 0;
            bool success = false;

            int maxId = Current.DB.Query<int?>("select max(Id) + 1 from Users").First() ?? 0;
            maxId += 1;

            while (!success)
            {
                IList<BusinessRuleViolation> violations = u.GetBusinessRuleViolations(ChangeAction.Insert);

                if (violations.Any(v => v.PropertyName == "Login"))
                {
                    u.Login = login + (maxId + retries);
                }
                else if (violations.Count > 0)
                {
                    throw new NotImplementedException("The User isn't valid, and we can't compensate for it right now.");
                }
                else
                {
                    success = true;
                }
            }

            u.Id = Current.DB.Users.Insert(new { u.Email, u.Login, u.CreationDate }).Value;
            Current.DB.UserOpenIds.Insert(new { OpenIdClaim = openIdClaim, UserId = u.Id });

            return u;
        }
        /// <summary>
        /// initializes current user based on the current Request's cookies/authentication status. This
        /// method could return a newly created, Anonymous User if no means of identification are found.
        /// </summary>
        protected void InitCurrentUser()
        {
            _currentUser = new User();
            _currentUser.IsAnonymous = true;

            if (Request.IsAuthenticated)
            {
                int id;
                if (Int32.TryParse(User.Identity.Name, out id))
                {
                    User lookup = Current.DB.Users.FirstOrDefault(u => u.Id == id);
                    if (lookup != null)
                    {
                        _currentUser = lookup;
                        _currentUser.IsAnonymous = false;
                    }
                }
                else
                {
                    FormsAuthentication.SignOut();
                }
            }

            _currentUser.IPAddress = Request.UserHostAddress;
        }
 partial void DeleteUser(User instance);
 partial void UpdateUser(User instance);
 partial void InsertUser(User instance);
        private int? FindUserId(User user)
        {
            if (!user.IsAnonymous && user.Email != null)
            {

                using (SqlConnection cnn = GetConnection())
                {
                    string hash = Util.GravatarHash(user.Email);
                    cnn.Open();
                    SqlCommand cmd = cnn.CreateCommand();
                    cmd.CommandText = "select top 1 Id from Users where EmailHash = @EmailHash order by Reputation desc";
                    SqlParameter p = cmd.Parameters.Add("@EmailHash", SqlDbType.NVarChar);
                    p.Value = hash;
                    try
                    {
                        return (int?)cmd.ExecuteScalar();
                    }
                    catch
                    {
                        // allow this to fail, its not critical
                    }
                }
            }
            return null;
        }
 /// <summary>
 /// initializes current user based on the current Request's cookies/authentication status. This
 /// method could return a newly created, Anonymous User if no means of identification are found.
 /// </summary>
 protected void InitCurrentUser()
 {
     _currentUser = GetCurrentUser(Request, User.Identity.Name);
 }
        private static User GetCurrentUser(bool isAuthenticated, string userHostAddress, string identity)
        {
            var user = new User();
            user.IsAnonymous = true;

            if (isAuthenticated)
            {
                int id;
                if (Int32.TryParse(identity, out id))
                {
                    User lookup = Current.DB.Users.Get(id);
                    if (lookup != null)
                    {
                        user = lookup;
                        user.IsAnonymous = false;
                    }
                }
                else
                {
                    FormsAuthentication.SignOut();
                }
            }

            user.IPAddress = userHostAddress;
            return user;
        }
        public static User CreateUser(string login, string email, string openIdClaim)
        {
            var u = new User();
            u.CreationDate = DateTime.UtcNow;

            login = CleanLogin(login ?? string.Empty);

            if (login.Length == 0)
            {
                /* email scrubbing got people upset, so it is gone now
                if (email != null)
                    login = CleanLogin(email.Split('@')[0]);
                 */

                if (login.Length == 0)
                    login = emptyLogin;
            }

            u.Login = login;
            u.Email = email;

            int retries = 0;
            bool success = false;

            int maxId = Current.DB.ExecuteQuery<int?>("select max(Id) + 1 from Users").First() ?? 0;
            maxId += 1;

            while (!success)
            {
                IList<BusinessRuleViolation> violations = u.GetBusinessRuleViolations(ChangeAction.Insert);

                if (violations.Any(v => v.PropertyName == "Login"))
                {
                    u.Login = login + (maxId + retries);
                }
                else if (violations.Count > 0)
                {
                    throw new NotImplementedException("The User isn't valid, and we can't compensate for it right now.");
                }
                else
                {
                    success = true;
                }
            }

            Current.DB.Users.InsertOnSubmit(u);

            var o = new UserOpenId();
            o.OpenIdClaim = openIdClaim;
            o.User = u;

            Current.DB.UserOpenIds.InsertOnSubmit(o);
            Current.DB.SubmitChanges();
            return u;
        }
Example #19
0
        public int? GuessUserId(User user)
        {
            if (user.IsAnonymous || !AppSettings.GuessUserId) return null;

            string cacheKey = "UserIdForSite" + Id + "_" + user.Id;

            var currentId = HttpContext.Current.Session[cacheKey] as int?;
            if (currentId == null)
            {
                int? id = FindUserId(user);
                if (id != null)
                {
                    HttpContext.Current.Cache[cacheKey] = id;
                }
            }

            currentId = HttpContext.Current.Cache[cacheKey] as int?;
            if (currentId == null)
            {
                HttpContext.Current.Cache[cacheKey] = -1;
            }

            return currentId != -1 ? currentId : null;
        }
        public static AsyncResult Execute(ParsedQuery query, User user, Site site, QueryContextData context)
        {
            string userTag = user.IsAnonymous ? user.IPAddress : user.Id.ToString();

            List<Task> activeTasks;
            running.TryGetValue(userTag, out activeTasks);
            if (activeTasks != null)
            {
                lock(activeTasks)
                {
                    if (activeTasks.Where(t => !t.IsCompleted).Count() >= AppSettings.ConcurrentQueries)
                    {
                        throw new ApplicationException("Too many queries are running, you may only run " + AppSettings.ConcurrentQueries + " queries at a time");
                    }
                }
            }
            else
            {
                running.TryAdd(userTag, new List<Task>());
                activeTasks = running[userTag];
            }

            AsyncResult result = new AsyncResult
            {
                JobId = Guid.NewGuid(),
                State = AsyncState.Pending,
                ParsedQuery = query,
                Site = site, 
                LastPoll = DateTime.UtcNow,
                QueryContextData = context
            };

            Task task = new Task(() =>
            {
                try
                {
                    result.QueryResults = QueryRunner.GetResults(query, site, user, result);

                    if (result.State == AsyncState.Pending)
                    {
                        result.State = AsyncState.Success;
                    }
                }
                catch (Exception e)
                {                    
                    result.Exception = e;
                    result.State = AsyncState.Failure;
                }
            });

            task.ContinueWith(ignore => {
                result.CompletionDate = DateTime.UtcNow;

                lock (activeTasks) 
                {
                    activeTasks.RemoveAll(t => t.IsCompleted);
                }
            });

            result.Task = task;

            jobs.TryAdd(result.JobId, result);
            task.Start();
            lock(activeTasks)
            {
                activeTasks.Add(task);
            }

            // give it some time to get results ... 
            System.Threading.Thread.Sleep(50);

            return result;
        }
        public static void LogRevisionExecution(User user, int siteId, int revisionId)
        {

            int updated = Current.DB.Query<int>(@"
                UPDATE RevisionExecutions SET
                    ExecutionCount = ExecutionCount + 1,
                    LastRun = @last
                WHERE
                    RevisionId = @revision AND
                    SiteId = @site AND
                    UserId " + (user.IsAnonymous ? "IS NULL" : "= @user") + @"

                SELECT @@ROWCOUNT",
                new
                {
                    revision = revisionId,
                    site = siteId,
                    user = user.Id,
                    last = DateTime.UtcNow
                }
            ).FirstOrDefault();

            if (updated == 0)
            {
                Current.DB.Execute(@"
                    INSERT INTO RevisionExecutions(
                        ExecutionCount, FirstRun, LastRun,
                        RevisionId, SiteId, UserId
                    ) VALUES(
                        1, @first, @last, @revision, @site, @user
                    )",
                    new
                    {
                        first = DateTime.UtcNow,
                        last = DateTime.UtcNow,
                        revision = revisionId,
                        site = siteId,
                        user = (user.IsAnonymous ? (int?)null : user.Id)
                    }
                );
            }
        }
        private void IssueFormsTicket(User user)
        {
            var ticket = new FormsAuthenticationTicket(
                1,
                user.Id.ToString(),
                DateTime.Now,
                DateTime.Now.AddYears(2),
                true,
                "");

            string encryptedTicket = FormsAuthentication.Encrypt(ticket);

            var authenticationCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket)
            {
                Expires = ticket.Expiration,
                HttpOnly = true,
                Secure = Current.IsSecureConnection
            };
            Response.Cookies.Add(authenticationCookie);
        }
 public static QueryResults GetResults(ParsedQuery query, Site site, User user, AsyncQueryRunner.AsyncResult result = null)
 {
     if (query.TargetSites != TargetSites.Current)
     {
         return GetMultiSiteResults(query, user, result);
     }
     else
     {
         return GetSingleSiteResults(query, site, user, result);
     }
 }
        public static QueryResults GetMultiSiteResults(ParsedQuery parsedQuery, User currentUser)
        {
            var sites = Current.DB.Sites.ToList();
            if (parsedQuery.ExcludesMetas)
            {
                sites = sites.Where(s => !s.Url.Contains("meta.")).ToList();
            }

            var firstSite = sites.First();
            var results = QueryRunner.GetSingleSiteResults(parsedQuery, firstSite, currentUser);
            StringBuilder buffer = new StringBuilder();

            if (results.ResultSets.First().Columns.Where(c => c.Name == "Pivot").Any())
            {
                foreach (var info in results.ResultSets.First().Columns)
                {
                    if (info.Name == "Pivot")
                    {
                        info.Name = firstSite.Name + " Pivot";
                        break;
                    }
                }

                foreach (var s in sites.Skip(1))
                {
                    try
                    {
                        var tmp = QueryRunner.GetSingleSiteResults(parsedQuery, s, currentUser);
                        results.ExecutionTime += tmp.ExecutionTime;
                        MergePivot(s, results, tmp);
                    }
                    catch (Exception e)
                    {
                        // don't blow up here ... just skip the site.
                    }
                }
            }
            else
            {
                results = results.ToTextResults();
                AddBody(buffer, results, firstSite);
                foreach (var s in sites.Skip(1))
                {
                    try
                    {
                        var tmp = QueryRunner.GetSingleSiteResults(parsedQuery, s, currentUser).ToTextResults();
                        results.ExecutionTime += tmp.ExecutionTime;
                        AddBody(buffer, tmp, s);
                    }
                    catch (Exception e)
                    {
                        // don't blow up ... just skip the site
                    }

                }
            }

            results.Messages = buffer.ToString();
            results.MultiSite = true;
            results.ExcludeMetas = parsedQuery.ExcludesMetas;

            return results;
        }
        private static QueryResults GetMultiSiteResults(ParsedQuery parsedQuery, User currentUser, AsyncQueryRunner.AsyncResult result = null)
        {
            var sites = Current.DB.Sites.All();
            if (parsedQuery.TargetSites == TargetSites.AllNonMetaSites)
            { 
                sites = sites.Where(s => !s.Url.Contains("meta.")).ToList();
            }
            else if (parsedQuery.TargetSites == TargetSites.AllMetaSites)
            {
                sites = sites.Where(s => s.Url.Contains("meta.")).ToList();
            }
            else if (parsedQuery.TargetSites == TargetSites.AllNonMetaSitesButSO)
            {
                sites = sites.Where(s => !s.Url.Contains("meta.") && !s.Url.Contains("stackoverflow.")).ToList();
            }
            else if (parsedQuery.TargetSites == TargetSites.AllMetaSitesButMSO)
            {
                sites = sites.Where(s => s.Url.Contains("meta.") && !s.Url.Contains("stackoverflow.")).ToList();
            }

            var firstSite = sites.First();
            var results = QueryRunner.GetSingleSiteResults(parsedQuery, firstSite, currentUser, result);

            if (results.ResultSets.First().Columns.Where(c => c.Name == "Pivot").Any())
            {
                foreach (var info in results.ResultSets.First().Columns)
                {
                    if (info.Name == "Pivot")
                    {
                        info.Name = firstSite.Name + " Pivot";
                        break;
                    }
                }

                foreach (var s in sites.Skip(1))
                {
                    try
                    {
                        var tmp = QueryRunner.GetSingleSiteResults(parsedQuery, s, currentUser);
                        results.ExecutionTime += tmp.ExecutionTime;
                        MergePivot(s, results, tmp);
                    }
                    catch (Exception)
                    { 
                        // don't blow up here ... just skip the site.
                    }
                }
            }
            else
            {

                results.ResultSets[0].Columns.Add(new ResultColumnInfo { Name = "Site Name", Type = ResultColumnType.Site });
                foreach (var row in results.ResultSets[0].Rows)
                {
                    row.Add(sites.First().SiteInfo);
                }
                
                foreach (var s in sites.Skip(1))
                {
                    if (result != null && result.Cancelled)
                    {
                        break;
                    }

                    try
                    {
                        var tmp = QueryRunner.GetSingleSiteResults(parsedQuery, s, currentUser, result);

                        foreach (var row in tmp.ResultSets[0].Rows)
                        {
                            row.Add(s.SiteInfo);
                            results.ResultSets[0].Rows.Add(row);
                        }

                        results.ExecutionTime += tmp.ExecutionTime;
                        results.Messages += "\n" + tmp.Messages;
                    }
                    catch (Exception)
                    { 
                        // don't blow up ... just skip the site
                    }

                }
            }

            results.TargetSites = parsedQuery.TargetSites;

            return results;
        }
        public static QueryResults GetSingleSiteResults(ParsedQuery query, Site site, User user)
        {
            QueryResults results = null;
            var timer = new Stopwatch();

            timer.Start();

            var cache = QueryUtil.GetCachedResults(query, site.Id);

            if (cache != null)
            {
                if (!query.IncludeExecutionPlan || cache.ExecutionPlan != null)
                {
                    results = new QueryResults();
                    results.WithCache(cache);
                    results.Truncated = cache.Truncated;
                    results.Messages = cache.Messages;
                    results.FromCache = true;

                    // If we didn't ask for the execution plan, don't return it
                    if (!query.IncludeExecutionPlan)
                    {
                        results.ExecutionPlan = null;
                    }
                }
            }

            timer.Stop();

            if (results == null)
            {
                results = ExecuteNonCached(query, site, user);
                results.FromCache = false;

                AddResultToCache(results, query, site, cache != null);
            }
            else
            {
                results.ExecutionTime = timer.ElapsedMilliseconds;
            }

            results.Url = site.Url;
            results.SiteId = site.Id;
            results.SiteName = site.Name.ToLower();

            return results;
        }