New Item Form with DataFormWebpart

Feb 22, 2013 at 7:59 PM
Hi, I ran into an issue with the reCAPTCHA and the DataFormWebPart . reCAPTCHA works wonderfully in the older, but stock ListFormWebPart . For some reason, the DataFormWebPart does two post-backs, each with a validation step.

For most field types this isn't a problem, but the recAPTCHA regenerates the validation data every time the page refreshed (or posted back).

My fix was to add some caching to the validation check. Since the challenge string doesn't change between calls, I used this as the index to see if the challenge had already been validated. I modified reCAPTCHAUserControl.IsValid(string) and added a couple of items. When ever a reCAPTCHA field is validated, old items in the cache are removed. I set a limit of 5 minutes, but it really wouldn't need to be longer than that. A time thread would be an improvement to the cache cleanup, but that's for another day.

Here's the code:
class TestedChallenge
{
    public string challenge;
    public bool successful;
    public DateTime when = DateTime.Now;

    public TestedChallenge(string challenge, bool successful) {
        this.challenge = challenge;
        this.successful = successful;
    }
}

static List<TestedChallenge> challenges = new List<TestedChallenge>();

static bool IsTested(string challenge) {
    bool ret = false;

    lock (challenges) {
        for (int x=challenges.Count - 1; x >= 0; --x) {
            TestedChallenge c = challenges[x];
            if (c.challenge == challenge)
                ret = c.successful;

            if (c.when.AddMinutes(5) < DateTime.Now) // free entries from the cache
                challenges.RemoveAt(x);
        }
    }

    return ret;
}

public static bool IsValid(string privateKey) {
    string challenge = HttpContext.Current.Request["recaptcha_challenge_field"];
    bool valid = IsTested(challenge);
    if (!valid) {
        string req = "privatekey=" + privateKey + "&remoteip=" + HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"] + "&challenge=" + challenge + "&response=" + HttpContext.Current.Request["recaptcha_response_field"];
        byte[] post = Encoding.ASCII.GetBytes(req);

        HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://www.google.com/recaptcha/api/verify");
        request.Method = "POST";
        request.ContentType = "application/x-www-form-urlencoded";
        request.ContentLength = post.Length;
        Stream requestStream = request.GetRequestStream();
        requestStream.Write(post, 0, post.Length);
        requestStream.Close();
        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        string resp = new StreamReader(response.GetResponseStream()).ReadToEnd();
        valid = resp.StartsWith("true");

        if (valid) {
            lock (challenges) {
                challenges.Add(new TestedChallenge(challenge, true));
            }
        }
    }
    return valid;
}