PageRenderTime 66ms CodeModel.GetById 48ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 0ms

/BlogEngine/BlogEngine.NET/App_Code/Extensions/Recaptcha/RecaptchaControl.cs

#
C# | 619 lines | 386 code | 73 blank | 160 comment | 25 complexity | 5da3ecdda507a1cbb6b2075551288858 MD5 | raw file
  1// Copyright (c) 2007 Adrian Godong, Ben Maurer
  2// Permission is hereby granted, free of charge, to any person obtaining a copy
  3// of this software and associated documentation files (the "Software"), to deal
  4// in the Software without restriction, including without limitation the rights
  5// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  6// copies of the Software, and to permit persons to whom the Software is
  7// furnished to do so, subject to the following conditions:
  8// The above copyright notice and this permission notice shall be included in
  9// all copies or substantial portions of the Software.
 10// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 11// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 12// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 13// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 14// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 15// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 16// THE SOFTWARE.
 17
 18// Adapted for dotnetblogengine by Filip Stanek ( http://www.bloodforge.com )
 19
 20namespace App_Code.Controls
 21{
 22    using System;
 23    using System.Web;
 24    using System.Web.Caching;
 25    using System.Web.UI;
 26    using System.Web.UI.WebControls;
 27
 28    using BlogEngine.Core;
 29    using BlogEngine.Core.Web.Extensions;
 30
 31    using Recaptcha;
 32
 33    /// <summary>
 34    /// The recaptcha control.
 35    /// </summary>
 36    public class RecaptchaControl : WebControl, IValidator
 37    {
 38        #region Constants and Fields
 39
 40        /// <summary>
 41        ///     The recaptcha challenge field.
 42        /// </summary>
 43        private const string RecaptchaChallengeField = "recaptcha_challenge_field";
 44
 45        /*
 46        /// <summary>
 47        /// The recaptcha host.
 48        /// </summary>
 49        private const string RecaptchaHost = "http://api.recaptcha.net";
 50        */
 51
 52        /// <summary>
 53        ///     The recaptcha response field.
 54        /// </summary>
 55        private const string RecaptchaResponseField = "recaptcha_response_field";
 56
 57        /*
 58        /// <summary>
 59        /// The recaptcha secure host.
 60        /// </summary>
 61        private const string RecaptchaSecureHost = "https://api-secure.recaptcha.net";
 62        */
 63
 64        /// <summary>
 65        ///     The error message.
 66        /// </summary>
 67        private string errorMessage;
 68
 69        /// <summary>
 70        ///     The private key.
 71        /// </summary>
 72        private string privateKey;
 73
 74        /// <summary>
 75        ///     The public key.
 76        /// </summary>
 77        private string publicKey;
 78
 79        /// <summary>
 80        ///     The recaptcha response.
 81        /// </summary>
 82        private RecaptchaResponse recaptchaResponse;
 83
 84        /// <summary>
 85        ///     The skip recaptcha.
 86        /// </summary>
 87        private bool skipRecaptcha = true;
 88
 89        #endregion
 90
 91        #region Properties
 92
 93        /// <summary>
 94        ///     Gets or sets ErrorMessage.
 95        /// </summary>
 96        public string ErrorMessage
 97        {
 98            get
 99            {
100                return this.errorMessage ?? "The verification words are incorrect.";
101            }
102
103            set
104            {
105                this.errorMessage = value;
106            }
107        }
108
109        /// <summary>
110        ///     Gets or sets a value indicating whether IsValid.
111        /// </summary>
112        public bool IsValid
113        {
114            get
115            {
116                return this.recaptchaResponse != null && this.recaptchaResponse.IsValid;
117            }
118
119            set
120            {
121            }
122        }
123
124        /// <summary>
125        ///     Gets MaxLogEntries.
126        /// </summary>
127        public int MaxLogEntries
128        {
129            get
130            {
131                var settings = ExtensionManager.GetSettings("Recaptcha");
132                return Convert.ToInt32(settings.GetSingleValue("MaxLogEntries"));
133            }
134        }
135
136        /// <summary>
137        ///     Gets a value indicating whether the control has been enabled via the Extension Manager
138        /// </summary>
139        public bool RecaptchaEnabled
140        {
141            get
142            {
143                var captchaExtension = ExtensionManager.GetExtension("Recaptcha");
144                return captchaExtension.Enabled;
145            }
146        }
147
148        /// <summary>
149        ///     Gets a value indicating whether RecaptchaLoggingEnabled.
150        /// </summary>
151        public bool RecaptchaLoggingEnabled
152        {
153            get
154            {
155                ExtensionManager.GetSettings("Recaptcha");
156                return this.MaxLogEntries > 0;
157            }
158        }
159
160        /// <summary>
161        ///     Gets a value indicating whether the recaptcha needs to be displayed for the current user
162        /// </summary>
163        public bool RecaptchaNecessary
164        {
165            get
166            {
167                var settings = ExtensionManager.GetSettings("Recaptcha");
168                return !Security.IsAuthenticated ||
169                       Convert.ToBoolean(settings.GetSingleValue("ShowForAuthenticatedUsers"));
170            }
171        }
172
173        /// <summary>
174        ///     Gets or sets Theme.
175        /// </summary>
176        public string Theme { get; set; }
177
178        /// <summary>
179        ///     Gets or sets Language.
180        /// </summary>
181        public string Language { get; set; }
182
183        /// <summary>
184        ///     Gets or sets UserUniqueIdentifier.
185        /// </summary>
186        public string UserUniqueIdentifier { get; set; }
187
188        /// <summary>
189        ///     Gets or sets PageLoadTime.
190        /// </summary>
191        internal DateTime PageLoadTime
192        {
193            get
194            {
195                return Blog.CurrentInstance.Cache[string.Format("{0}PageLoadTime", this.UserUniqueIdentifier)] != null
196                           ? Convert.ToDateTime(
197                               Blog.CurrentInstance.Cache[string.Format("{0}PageLoadTime", this.UserUniqueIdentifier)])
198                           : DateTime.Now;
199            }
200
201            set
202            {
203                if (Blog.CurrentInstance.Cache[string.Format("{0}PageLoadTime", this.UserUniqueIdentifier)] != null)
204                {
205                    Blog.CurrentInstance.Cache[string.Format("{0}PageLoadTime", this.UserUniqueIdentifier)] = value;
206                }
207                else
208                {
209                    Blog.CurrentInstance.Cache.Add(
210                        string.Format("{0}PageLoadTime", this.UserUniqueIdentifier), 
211                        value, 
212                        null, 
213                        Cache.NoAbsoluteExpiration, 
214                        new TimeSpan(1, 0, 0), 
215                        CacheItemPriority.Low, 
216                        null);
217                }
218            }
219        }
220
221        /// <summary>
222        ///     Gets or sets RecaptchaAttempts.
223        /// </summary>
224        internal ushort RecaptchaAttempts
225        {
226            get
227            {
228                return Blog.CurrentInstance.Cache[string.Format("{0}RecaptchaAttempts", this.UserUniqueIdentifier)] !=
229                       null
230                           ? Convert.ToUInt16(
231                               Blog.CurrentInstance.Cache[string.Format("{0}RecaptchaAttempts", this.UserUniqueIdentifier)])
232                           : (ushort)0;
233            }
234
235            set
236            {
237                if (Blog.CurrentInstance.Cache[string.Format("{0}RecaptchaAttempts", this.UserUniqueIdentifier)] != null)
238                {
239                    Blog.CurrentInstance.Cache[string.Format("{0}RecaptchaAttempts", this.UserUniqueIdentifier)] = value;
240                }
241                else
242                {
243                    Blog.CurrentInstance.Cache.Add(
244                        string.Format("{0}RecaptchaAttempts", this.UserUniqueIdentifier), 
245                        value, 
246                        null, 
247                        Cache.NoAbsoluteExpiration, 
248                        new TimeSpan(0, 15, 0), 
249                        CacheItemPriority.Low, 
250                        null);
251                }
252            }
253        }
254
255        /// <summary>
256        ///     Gets or sets RecaptchaChallengeValue.
257        /// </summary>
258        internal string RecaptchaChallengeValue
259        {
260            get
261            {
262                return
263                    Blog.CurrentInstance.Cache[string.Format("{0}RecaptchaChallengeValue", this.UserUniqueIdentifier)] !=
264                    null
265                        ? Convert.ToString(
266                            Blog.CurrentInstance.Cache[string.Format("{0}RecaptchaChallengeValue", this.UserUniqueIdentifier)])
267                        : string.Empty;
268            }
269
270            set
271            {
272                if (Blog.CurrentInstance.Cache[string.Format("{0}RecaptchaChallengeValue", this.UserUniqueIdentifier)] !=
273                    null)
274                {
275                    Blog.CurrentInstance.Cache[string.Format("{0}RecaptchaChallengeValue", this.UserUniqueIdentifier)] =
276                        value;
277                }
278                else
279                {
280                    Blog.CurrentInstance.Cache.Add(
281                        string.Format("{0}RecaptchaChallengeValue", this.UserUniqueIdentifier), 
282                        value, 
283                        null, 
284                        Cache.NoAbsoluteExpiration, 
285                        new TimeSpan(0, 1, 0), 
286                        CacheItemPriority.NotRemovable, 
287                        null);
288                }
289            }
290        }
291
292        /// <summary>
293        ///     Gets or sets RecaptchaRenderTime.
294        /// </summary>
295        internal DateTime RecaptchaRenderTime
296        {
297            get
298            {
299                return Blog.CurrentInstance.Cache[string.Format("{0}RecaptchaRenderTime", this.UserUniqueIdentifier)] !=
300                       null
301                           ? Convert.ToDateTime(
302                               Blog.CurrentInstance.Cache[string.Format("{0}RecaptchaRenderTime", this.UserUniqueIdentifier)])
303                           : DateTime.Now;
304            }
305
306            set
307            {
308                if (Blog.CurrentInstance.Cache[string.Format("{0}RecaptchaRenderTime", this.UserUniqueIdentifier)] !=
309                    null)
310                {
311                    Blog.CurrentInstance.Cache[string.Format("{0}RecaptchaRenderTime", this.UserUniqueIdentifier)] =
312                        value;
313                }
314                else
315                {
316                    Blog.CurrentInstance.Cache.Add(
317                        string.Format("{0}RecaptchaRenderTime", this.UserUniqueIdentifier), 
318                        value, 
319                        null, 
320                        Cache.NoAbsoluteExpiration, 
321                        new TimeSpan(1, 0, 0), 
322                        CacheItemPriority.Low, 
323                        null);
324                }
325            }
326        }
327
328        /// <summary>
329        ///     Gets or sets RecaptchaResponseValue.
330        /// </summary>
331        internal string RecaptchaResponseValue
332        {
333            get
334            {
335                return
336                    Blog.CurrentInstance.Cache[string.Format("{0}RecaptchaResponseValue", this.UserUniqueIdentifier)] !=
337                    null
338                        ? Convert.ToString(
339                            Blog.CurrentInstance.Cache[string.Format("{0}RecaptchaResponseValue", this.UserUniqueIdentifier)])
340                        : string.Empty;
341            }
342
343            set
344            {
345                if (Blog.CurrentInstance.Cache[string.Format("{0}RecaptchaResponseValue", this.UserUniqueIdentifier)] !=
346                    null)
347                {
348                    Blog.CurrentInstance.Cache[string.Format("{0}RecaptchaResponseValue", this.UserUniqueIdentifier)] =
349                        value;
350                }
351                else
352                {
353                    Blog.CurrentInstance.Cache.Add(
354                        string.Format("{0}RecaptchaResponseValue", this.UserUniqueIdentifier), 
355                        value, 
356                        null, 
357                        Cache.NoAbsoluteExpiration, 
358                        new TimeSpan(0, 1, 0), 
359                        CacheItemPriority.NotRemovable, 
360                        null);
361                }
362            }
363        }
364
365        #endregion
366
367        #region Public Methods
368
369        /// <summary>
370        /// The update log.
371        /// </summary>
372        /// <param name="comment">
373        /// The comment.
374        /// </param>
375        public void UpdateLog(Comment comment)
376        {
377            if (!this.RecaptchaLoggingEnabled || this.skipRecaptcha)
378            {
379                return;
380            }
381
382            var log = RecaptchaLogger.ReadLogItems();
383
384            var logItem = new RecaptchaLogItem
385                {
386                    Response = this.RecaptchaResponseValue, 
387                    Challenge = this.RecaptchaChallengeValue, 
388                    CommentId = comment.Id, 
389                    Enabled = this.RecaptchaEnabled, 
390                    Necessary = this.RecaptchaNecessary, 
391                    NumberOfAttempts = this.RecaptchaAttempts, 
392                    TimeToComment = DateTime.Now.Subtract(this.PageLoadTime).TotalSeconds, 
393                    TimeToSolveCapcha = DateTime.Now.Subtract(this.RecaptchaRenderTime).TotalSeconds
394                };
395            log.Add(logItem);
396
397            if (log.Count > this.MaxLogEntries)
398            {
399                log.RemoveRange(0, log.Count - this.MaxLogEntries);
400            }
401
402            RecaptchaLogger.SaveLogItems(log);
403
404            this.RecaptchaAttempts = 0;
405            this.PageLoadTime = DateTime.Now;
406            Blog.CurrentInstance.Cache.Remove(string.Format("{0}RecaptchaChallengeValue", this.UserUniqueIdentifier));
407            Blog.CurrentInstance.Cache.Remove(string.Format("{0}RecaptchaResponseValue", this.UserUniqueIdentifier));
408        }
409
410        /// <summary>
411        /// Validates the async.
412        /// </summary>
413        /// <param name="response">
414        /// The response.
415        /// </param>
416        /// <param name="challenge">
417        /// The challenge.
418        /// </param>
419        /// <returns>
420        /// Whether valid.
421        /// </returns>
422        public bool ValidateAsync(string response, string challenge)
423        {
424            if (this.RecaptchaLoggingEnabled)
425            {
426                this.RecaptchaAttempts++;
427            }
428
429            this.RecaptchaResponseValue = response;
430            this.RecaptchaChallengeValue = challenge;
431            this.Validate();
432            return this.IsValid;
433        }
434
435        #endregion
436
437        #region Implemented Interfaces
438
439        #region IValidator
440
441        /// <summary>
442        /// The validate.
443        /// </summary>
444        public void Validate()
445        {
446            if (this.skipRecaptcha)
447            {
448                this.recaptchaResponse = RecaptchaResponse.Valid;
449            }
450            else
451            {
452                var validator = new RecaptchaValidator
453                    {
454                       PrivateKey = this.privateKey, RemoteIP = this.Page.Request.UserHostAddress 
455                    };
456                if (String.IsNullOrEmpty(this.RecaptchaChallengeValue) &&
457                    String.IsNullOrEmpty(this.RecaptchaResponseValue))
458                {
459                    validator.Challenge = this.Context.Request.Form[RecaptchaChallengeField];
460                    validator.Response = this.Context.Request.Form[RecaptchaResponseField];
461                }
462                else
463                {
464                    validator.Challenge = this.RecaptchaChallengeValue;
465                    validator.Response = this.RecaptchaResponseValue;
466                }
467
468                this.recaptchaResponse = validator.Validate();
469            }
470        }
471
472        #endregion
473
474        #endregion
475
476        #region Methods
477
478        /// <summary>
479        /// Raises the <see cref="E:System.Web.UI.Control.Init"/> event.
480        /// </summary>
481        /// <param name="e">
482        /// An <see cref="T:System.EventArgs"/> object that contains the event data.
483        /// </param>
484        protected override void OnInit(EventArgs e)
485        {
486            base.OnInit(e);
487
488            var settings = ExtensionManager.GetSettings("Recaptcha");
489            this.publicKey = settings.GetSingleValue("PublicKey");
490            this.privateKey = settings.GetSingleValue("PrivateKey");
491
492            if (String.IsNullOrEmpty(this.Theme))
493            {
494                this.Theme = settings.GetSingleValue("Theme");
495            }
496
497            if (String.IsNullOrEmpty(this.Language))
498            {
499                this.Language = settings.GetSingleValue("Language");
500            }
501
502            if (this.RecaptchaEnabled && this.RecaptchaNecessary)
503            {
504                this.skipRecaptcha = false;
505            }
506
507            if (String.IsNullOrEmpty(this.publicKey) || String.IsNullOrEmpty(this.privateKey))
508            {
509                throw new ApplicationException("reCAPTCHA needs to be configured with a public & private key.");
510            }
511        }
512
513        /// <summary>
514        /// Raises the <see cref="E:System.Web.UI.Control.Unload"/> event.
515        /// </summary>
516        /// <param name="e">
517        /// An <see cref="T:System.EventArgs"/> object that contains event data.
518        /// </param>
519        protected override void OnUnload(EventArgs e)
520        {
521            if (this.RecaptchaLoggingEnabled)
522            {
523                if (!this.Page.IsCallback)
524                {
525                    this.PageLoadTime = DateTime.Now;
526                    this.RecaptchaAttempts = 0;
527                }
528
529                this.RecaptchaRenderTime = DateTime.Now;
530            }
531
532            base.OnUnload(e);
533        }
534
535        /// <summary>
536        /// Renders the control to the specified HTML writer.
537        /// </summary>
538        /// <param name="writer">
539        /// The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the control content.
540        /// </param>
541        protected override void Render(HtmlTextWriter writer)
542        {
543            if (!this.skipRecaptcha)
544            {
545                this.RenderContents(writer);
546            }
547        }
548
549        /// <summary>
550        /// Renders the contents.
551        /// </summary>
552        /// <param name="output">
553        /// The output.
554        /// </param>
555        protected override void RenderContents(HtmlTextWriter output)
556        {
557            output.AddAttribute("type", "text/javascript");
558            output.AddAttribute("src", "http://api.recaptcha.net/js/recaptcha_ajax.js");
559            output.RenderBeginTag("script");
560            output.RenderEndTag();
561
562            output.AddAttribute(HtmlTextWriterAttribute.Id, "spnCaptchaIncorrect");
563            output.AddAttribute(HtmlTextWriterAttribute.Style, "color:Red;display:none;");
564            output.RenderBeginTag("span");
565            output.WriteLine("The captcha text was not valid. Please try again.");
566            output.RenderEndTag();
567
568            output.AddAttribute(HtmlTextWriterAttribute.Id, "recaptcha_placeholder");
569            output.RenderBeginTag(HtmlTextWriterTag.Div);
570            output.RenderEndTag();
571
572            output.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
573            output.RenderBeginTag(HtmlTextWriterTag.Script);
574            output.WriteLine("function showRecaptcha() {");
575            output.WriteLine("Recaptcha.create('{0}', 'recaptcha_placeholder', {{", this.publicKey);
576            output.WriteLine("theme: '{0}',", this.Theme);
577            output.WriteLine("lang: '{0}',", this.Language);
578            output.WriteLine("tabindex: {0}", this.TabIndex);
579            output.WriteLine("})");
580            output.WriteLine("}");
581
582            output.WriteLine("var rc_oldonload = window.onload;");
583            output.WriteLine("if (typeof window.onload != 'function') {");
584            output.WriteLine("window.onload = showRecaptcha;");
585            output.WriteLine("}");
586            output.WriteLine("else {");
587            output.WriteLine("window.onload = function() {");
588            output.WriteLine("rc_oldonload();");
589            output.WriteLine("showRecaptcha();");
590            output.WriteLine("}");
591            output.WriteLine("}");
592
593            output.RenderEndTag();
594        }
595
596        #endregion
597
598        /*
599        /// <summary>
600        /// Generates the challenge URL.
601        /// </summary>
602        /// <param name="noscript">if set to <c>true</c> [no script].</param>
603        /// <returns>The challenge url.</returns>
604        private string GenerateChallengeUrl(bool noscript)
605        {
606            var urlBuilder = new StringBuilder();
607            urlBuilder.Append(this.Context.Request.IsSecureConnection ? RecaptchaSecureHost : RecaptchaHost);
608            urlBuilder.Append(noscript ? "/noscript?" : "/challenge?");
609            urlBuilder.AppendFormat("k={0}", this.publicKey);
610            if (this.recaptchaResponse != null && this.recaptchaResponse.ErrorCode != string.Empty)
611            {
612                urlBuilder.AppendFormat("&error={0}", this.recaptchaResponse.ErrorCode);
613            }
614
615            return urlBuilder.ToString();
616        }
617*/
618    }
619}