PageRenderTime 45ms CodeModel.GetById 13ms app.highlight 26ms RepoModel.GetById 1ms app.codeStats 1ms

/projects/WebControls/Captcha/CaptchaControl.vb

http://pigeoncms.googlecode.com/
Visual Basic | 573 lines | 468 code | 63 blank | 42 comment | 3 complexity | 6902a42233563ec1ec8025ddf3ba5f77 MD5 | raw file
  1Imports System.ComponentModel
  2Imports System.Web
  3Imports System.Web.UI
  4Imports System.Web.UI.WebControls
  5Imports System.Collections
  6Imports System.Collections.Specialized
  7
  8''' <summary>
  9''' CAPTCHA ASP.NET 2.0 user control
 10''' </summary>
 11''' <remarks>
 12''' add a reference to this DLL and add the CaptchaControl to your toolbox;
 13''' then just drag and drop the control on a web form and set properties on it.
 14'''
 15''' Jeff Atwood
 16''' http://www.codinghorror.com/
 17''' </remarks>
 18<DefaultProperty("Text")> _
 19Public Class CaptchaControl
 20    Inherits System.Web.UI.WebControls.WebControl
 21    Implements INamingContainer
 22    Implements IPostBackDataHandler
 23    Implements IValidator
 24
 25    Public Enum Layout
 26        Horizontal
 27        Vertical
 28    End Enum
 29
 30    Public Enum CacheType
 31        HttpRuntime
 32        Session
 33    End Enum
 34
 35    Private _timeoutSecondsMax As Integer = 90
 36    Private _timeoutSecondsMin As Integer = 3
 37    Private _userValidated As Boolean = True
 38    Private _text As String = "Enter the code shown:"
 39    Private _font As String = ""
 40    Private _captcha As CaptchaImage = New CaptchaImage
 41    Private _layoutStyle As Layout = Layout.Horizontal
 42    Private _prevguid As String
 43    Private _errorMessage As String = ""
 44    Private _cacheStrategy As CacheType = CacheType.HttpRuntime
 45
 46#Region "  Public Properties"
 47
 48    <Browsable(False), _
 49    Bindable(True), _
 50    Category("Appearance"), _
 51    DefaultValue("The text you typed does not match the text in the image."), _
 52    Description("Message to display in a Validation Summary when the CAPTCHA fails to validate.")> _
 53    Public Property ErrorMessage() As String Implements System.Web.UI.IValidator.ErrorMessage
 54        Get
 55            If Not _userValidated Then
 56                Return _errorMessage
 57            Else
 58                Return ""
 59            End If
 60        End Get
 61        Set(ByVal value As String)
 62            _errorMessage = value
 63        End Set
 64    End Property
 65
 66    <Browsable(False), _
 67    Category("Behavior"), _
 68    DefaultValue(True), _
 69    Description("Is Valid"), _
 70    DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
 71    Public Property IsValid() As Boolean Implements System.Web.UI.IValidator.IsValid
 72        Get
 73            Return _userValidated
 74        End Get
 75        Set(ByVal value As Boolean)
 76        End Set
 77    End Property
 78
 79    Public Overrides Property Enabled() As Boolean
 80        Get
 81            Return MyBase.Enabled
 82        End Get
 83        Set(ByVal value As Boolean)
 84            MyBase.Enabled = value
 85            ' When a validator is disabled, generally, the intent is not to
 86            ' make the page invalid for that round trip.
 87            If Not value Then
 88                _userValidated = True
 89            End If
 90        End Set
 91    End Property
 92
 93
 94    <DefaultValue("Enter the code shown above:"), _
 95    Description("Instructional text displayed next to CAPTCHA image."), _
 96    Category("Appearance")> _
 97    Public Property [Text]() As String
 98        Get
 99            Return _text
100        End Get
101        Set(ByVal Value As String)
102            _text = Value
103        End Set
104    End Property
105
106    <DefaultValue(GetType(CaptchaControl.Layout), "Horizontal"), _
107    Description("Determines if image and input area are displayed horizontally, or vertically."), _
108    Category("Captcha")> _
109    Public Property LayoutStyle() As Layout
110        Get
111            Return _layoutStyle
112        End Get
113        Set(ByVal Value As Layout)
114            _layoutStyle = Value
115        End Set
116    End Property
117
118    <DefaultValue(GetType(CaptchaControl.CacheType), "HttpRuntime"), _
119    Description("Determines if CAPTCHA codes are stored in HttpRuntime (fast, but local to current server) or Session (more portable across web farms)."), _
120    Category("Captcha")> _
121    Public Property CacheStrategy() As CacheType
122        Get
123            Return _cacheStrategy
124        End Get
125        Set(ByVal value As CacheType)
126            _cacheStrategy = value
127        End Set
128    End Property
129
130    <Description("Returns True if the user was CAPTCHA validated after a postback."), _
131    Category("Captcha")> _
132    Public ReadOnly Property UserValidated() As Boolean
133        Get
134            Return _userValidated
135        End Get
136    End Property
137
138
139    <DefaultValue(""), _
140    Description("Font used to render CAPTCHA text. If font name is blank, a random font will be chosen."), _
141    Category("Captcha")> _
142    Public Property CaptchaFont() As String
143        Get
144            Return _font
145        End Get
146        Set(ByVal Value As String)
147            _font = Value
148            _captcha.Font = _font
149        End Set
150    End Property
151
152    <DefaultValue(""), _
153    Description("Characters used to render CAPTCHA text. A character will be picked randomly from the string."), _
154    Category("Captcha")> _
155    Public Property CaptchaChars() As String
156        Get
157            Return _captcha.TextChars
158        End Get
159        Set(ByVal Value As String)
160            _captcha.TextChars = Value
161        End Set
162    End Property
163
164    <DefaultValue(5), _
165    Description("Number of CaptchaChars used in the CAPTCHA text"), _
166    Category("Captcha")> _
167    Public Property CaptchaLength() As Integer
168        Get
169            Return _captcha.TextLength
170        End Get
171        Set(ByVal Value As Integer)
172            _captcha.TextLength = Value
173        End Set
174    End Property
175
176    <DefaultValue(2), _
177    Description("Minimum number of seconds CAPTCHA must be displayed before it is valid. If you're too fast, you must be a robot. Set to zero to disable."), _
178    Category("Captcha")> _
179    Public Property CaptchaMinTimeout() As Integer
180        Get
181            Return _timeoutSecondsMin
182        End Get
183        Set(ByVal Value As Integer)
184            If Value > 15 Then
185                Throw New ArgumentOutOfRangeException("CaptchaTimeout", "Timeout must be less than 15 seconds. Humans aren't that slow!")
186            End If
187            _timeoutSecondsMin = Value
188        End Set
189    End Property
190
191    <DefaultValue(90), _
192    Description("Maximum number of seconds CAPTCHA will be cached and valid. If you're too slow, you may be a CAPTCHA hack attempt. Set to zero to disable."), _
193    Category("Captcha")> _
194    Public Property CaptchaMaxTimeout() As Integer
195        Get
196            Return _timeoutSecondsMax
197        End Get
198        Set(ByVal Value As Integer)
199            If Value < 15 And Value <> 0 Then
200                Throw New ArgumentOutOfRangeException("CaptchaTimeout", "Timeout must be greater than 15 seconds. Humans can't type that fast!")
201            End If
202            _timeoutSecondsMax = Value
203        End Set
204    End Property
205
206    <DefaultValue(50), _
207    Description("Height of generated CAPTCHA image."), _
208    Category("Captcha")> _
209    Public Property CaptchaHeight() As Integer
210        Get
211            Return _captcha.Height
212        End Get
213        Set(ByVal Value As Integer)
214            _captcha.Height = Value
215        End Set
216    End Property
217
218    <DefaultValue(180), _
219    Description("Width of generated CAPTCHA image."), _
220    Category("Captcha")> _
221    Public Property CaptchaWidth() As Integer
222        Get
223            Return _captcha.Width
224        End Get
225        Set(ByVal Value As Integer)
226            _captcha.Width = Value
227        End Set
228    End Property
229
230    <DefaultValue(GetType(CaptchaImage.FontWarpFactor), "Low"), _
231    Description("Amount of random font warping used on the CAPTCHA text"), _
232    Category("Captcha")> _
233    Public Property CaptchaFontWarping() As CaptchaImage.FontWarpFactor
234        Get
235            Return _captcha.FontWarp
236        End Get
237        Set(ByVal Value As CaptchaImage.FontWarpFactor)
238            _captcha.FontWarp = Value
239        End Set
240    End Property
241
242    <DefaultValue(GetType(CaptchaImage.BackgroundNoiseLevel), "Low"), _
243    Description("Amount of background noise to generate in the CAPTCHA image"), _
244    Category("Captcha")> _
245    Public Property CaptchaBackgroundNoise() As CaptchaImage.BackgroundNoiseLevel
246        Get
247            Return _captcha.BackgroundNoise
248        End Get
249        Set(ByVal Value As CaptchaImage.BackgroundNoiseLevel)
250            _captcha.BackgroundNoise = Value
251        End Set
252    End Property
253
254    <DefaultValue(GetType(CaptchaImage.LineNoiseLevel), "None"), _
255    Description("Add line noise to the CAPTCHA image"), _
256    Category("Captcha")> _
257    Public Property CaptchaLineNoise() As CaptchaImage.LineNoiseLevel
258        Get
259            Return _captcha.LineNoise
260        End Get
261        Set(ByVal Value As CaptchaImage.LineNoiseLevel)
262            _captcha.LineNoise = Value
263        End Set
264    End Property
265#End Region
266
267    Public Sub Validate() Implements System.Web.UI.IValidator.Validate
268        '-- a no-op, since we validate in LoadPostData
269    End Sub
270
271    Private Function GetCachedCaptcha(ByVal guid As String) As CaptchaImage
272        If _cacheStrategy = CacheType.HttpRuntime Then
273            Return CType(HttpRuntime.Cache.Get(guid), CaptchaImage)
274        Else
275            Return CType(HttpContext.Current.Session.Item(guid), CaptchaImage)
276        End If
277    End Function
278
279    Private Sub RemoveCachedCaptcha(ByVal guid As String)
280        If _cacheStrategy = CacheType.HttpRuntime Then
281            HttpRuntime.Cache.Remove(guid)
282        Else
283            HttpContext.Current.Session.Remove(guid)
284        End If
285    End Sub
286
287    ''' <summary>
288    ''' are we in design mode?
289    ''' </summary>
290    Private ReadOnly Property IsDesignMode() As Boolean
291        Get
292            Return HttpContext.Current Is Nothing
293        End Get
294    End Property
295
296    ''' <summary>
297    ''' Validate the user's text against the CAPTCHA text
298    ''' </summary>
299    Private Sub ValidateCaptcha(ByVal userEntry As String)
300
301        If Not Visible Or Not Enabled Then
302            _userValidated = True
303            Return
304        End If
305
306        '-- retrieve the previous captcha from the cache to inspect its properties
307        Dim ci As CaptchaImage = GetCachedCaptcha(_prevguid)
308        If ci Is Nothing Then
309            Me.ErrorMessage = "The code you typed has expired after " & Me.CaptchaMaxTimeout & " seconds."
310            _userValidated = False
311            Return
312        End If
313
314        '--  was it entered too quickly?
315        If Me.CaptchaMinTimeout > 0 Then
316            If (ci.RenderedAt.AddSeconds(Me.CaptchaMinTimeout) > Now) Then
317                _userValidated = False
318                Me.ErrorMessage = "Code was typed too quickly. Wait at least " & Me.CaptchaMinTimeout & " seconds."
319                RemoveCachedCaptcha(_prevguid)
320                Return
321            End If
322        End If
323
324        If String.Compare(userEntry, ci.Text, True) <> 0 Then
325            Me.ErrorMessage = "The code you typed does not match the code in the image."
326            _userValidated = False
327            RemoveCachedCaptcha(_prevguid)
328            Return
329        End If
330
331        _userValidated = True
332        RemoveCachedCaptcha(_prevguid)
333    End Sub
334
335    ''' <summary>
336    ''' returns HTML-ized color strings
337    ''' </summary>
338    Private Function HtmlColor(ByVal color As Drawing.Color) As String
339        If color.IsEmpty Then Return ""
340        If color.IsNamedColor Then
341            Return color.ToKnownColor.ToString
342        End If
343        If color.IsSystemColor Then
344            Return color.ToString
345        End If
346        Return "#" & color.ToArgb.ToString("x").Substring(2)
347    End Function
348
349    ''' <summary>
350    ''' returns css "style=" tag for this control
351    ''' based on standard control visual properties
352    ''' </summary>
353    Private Function CssStyle() As String
354        Dim sb As New System.Text.StringBuilder
355        Dim strColor As String
356
357        With sb
358            .Append(" style='")
359
360            If BorderWidth.ToString.Length > 0 Then
361                .Append("border-width:")
362                .Append(BorderWidth.ToString)
363                .Append(";")
364            End If
365            If BorderStyle <> WebControls.BorderStyle.NotSet Then
366                .Append("border-style:")
367                .Append(BorderStyle.ToString)
368                .Append(";")
369            End If
370            strColor = HtmlColor(BorderColor)
371            If strColor.Length > 0 Then
372                .Append("border-color:")
373                .Append(strColor)
374                .Append(";")
375            End If
376
377            strColor = HtmlColor(BackColor)
378            If strColor.Length > 0 Then
379                .Append("background-color:" & strColor & ";")
380            End If
381
382            strColor = HtmlColor(ForeColor)
383            If strColor.Length > 0 Then
384                .Append("color:" & strColor & ";")
385            End If
386
387            If Font.Bold Then
388                .Append("font-weight:bold;")
389            End If
390
391            If Font.Italic Then
392                .Append("font-style:italic;")
393            End If
394
395            If Font.Underline Then
396                .Append("text-decoration:underline;")
397            End If
398
399            If Font.Strikeout Then
400                .Append("text-decoration:line-through;")
401            End If
402
403            If Font.Overline Then
404                .Append("text-decoration:overline;")
405            End If
406
407            If Font.Size.ToString.Length > 0 Then
408                .Append("font-size:" & Font.Size.ToString & ";")
409            End If
410
411            If Font.Names.Length > 0 Then
412                Dim strFontFamily As String
413                .Append("font-family:")
414                For Each strFontFamily In Font.Names
415                    .Append(strFontFamily)
416                    .Append(",")
417                Next
418                .Length = .Length - 1
419                .Append(";")
420            End If
421
422            If Height.ToString <> "" Then
423                .Append("height:" & Height.ToString & ";")
424            End If
425            If Width.ToString <> "" Then
426                .Append("width:" & Width.ToString & ";")
427            End If
428
429            .Append("'")
430        End With
431        If sb.ToString = " style=''" Then
432            Return ""
433        Else
434            Return sb.ToString
435        End If
436    End Function
437
438    ''' <summary>
439    ''' render raw control HTML to the page
440    ''' </summary>
441    Protected Overrides Sub Render(ByVal Output As HtmlTextWriter)
442        With Output
443            '-- master DIV
444            .Write("<div")
445            If CssClass <> "" Then
446                .Write(" class='" & CssClass & "'")
447            End If
448            .Write(CssStyle)
449            .Write(">")
450
451            '-- image DIV/SPAN
452            If Me.LayoutStyle = Layout.Vertical Then
453                .Write("<div style='text-align:center;margin:5px;'>")
454            Else
455                .Write("<span style='margin:5px;float:left;'>")
456            End If
457            '-- this is the URL that triggers the CaptchaImageHandler
458            If Not IsDesignMode Then
459                .Write("<img src='" & VirtualPathUtility.ToAbsolute("~/") & "Handlers/CaptchaImage.aspx")
460                .Write("?guid=" & Convert.ToString(_captcha.UniqueId))
461            Else
462                .Write("<img src='CaptchaControl.bmp?1=1")
463            End If
464            If Me.CacheStrategy = CacheType.Session Then
465                .Write("&s=1")
466            End If
467            .Write("' border='0'")
468            If ToolTip.Length > 0 Then
469                .Write(" alt='" & ToolTip & "'")
470            End If
471            .Write(" width=" & _captcha.Width)
472            .Write(" height=" & _captcha.Height)
473            .Write(">")
474            If Me.LayoutStyle = Layout.Vertical Then
475                .Write("</div>")
476            Else
477                .Write("</span>")
478            End If
479
480            '-- text input and submit button DIV/SPAN
481            If Me.LayoutStyle = Layout.Vertical Then
482                .Write("<div style='text-align:center;margin:5px;'>")
483            Else
484                .Write("<span style='margin:5px;float:left;'>")
485            End If
486            If _text.Length > 0 Then
487                .Write(_text)
488                .Write("<br />")
489            End If
490            .Write("<input name=" & UniqueID & " type='text' size=")
491            .Write(_captcha.TextLength.ToString)
492            .Write(" maxlength=")
493            .Write(_captcha.TextLength.ToString)
494            If AccessKey.Length > 0 Then
495                .Write(" accesskey=" & AccessKey)
496            End If
497            If Not Enabled Then
498                .Write(" disabled=""disabled""")
499            End If
500            If TabIndex > 0 Then
501                .Write(" tabindex=" & TabIndex.ToString)
502            End If
503            .Write(" value=''>")
504            If Me.LayoutStyle = Layout.Vertical Then
505                .Write("</div>")
506            Else
507                .Write("</span>")
508                .Write("<br clear='all'>")
509            End If
510
511            '-- closing tag for master DIV
512            .Write("</div>")
513        End With
514    End Sub
515
516    ''' <summary>
517    ''' generate a new captcha and store it in the ASP.NET Cache by unique GUID
518    ''' </summary>
519    Private Sub GenerateNewCaptcha()
520        If Not IsDesignMode Then
521            If _cacheStrategy = CacheType.HttpRuntime Then
522                HttpRuntime.Cache.Add(_captcha.UniqueId, _captcha, Nothing, _
523                    DateTime.Now.AddSeconds(Convert.ToDouble(IIf(Me.CaptchaMaxTimeout = 0, 90, Me.CaptchaMaxTimeout))), _
524                    TimeSpan.Zero, Caching.CacheItemPriority.NotRemovable, Nothing)
525            Else
526                HttpContext.Current.Session.Add(_captcha.UniqueId, _captcha)
527            End If
528        End If
529    End Sub
530
531    ''' <summary>
532    ''' Retrieve the user's CAPTCHA input from the posted data
533    ''' </summary>
534    Public Function LoadPostData(ByVal PostDataKey As String, ByVal Values As NameValueCollection) As Boolean Implements IPostBackDataHandler.LoadPostData
535        ValidateCaptcha(Convert.ToString(Values(Me.UniqueID)))
536        Return False
537    End Function
538
539    Public Sub RaisePostDataChangedEvent() Implements IPostBackDataHandler.RaisePostDataChangedEvent
540    End Sub
541
542    Protected Overrides Function SaveControlState() As Object
543        Return CType(_captcha.UniqueId, Object)
544    End Function
545
546    Protected Overrides Sub LoadControlState(ByVal state As Object)
547        If state IsNot Nothing Then
548            _prevguid = CType(state, String)
549        End If
550    End Sub
551
552    Protected Overrides Sub OnInit(ByVal e As System.EventArgs)
553        MyBase.OnInit(e)
554        Page.RegisterRequiresControlState(Me)
555        Page.Validators.Add(Me)
556
557    End Sub
558
559    Protected Overrides Sub OnUnload(ByVal e As System.EventArgs)
560        If Not (Page Is Nothing) Then
561            Page.Validators.Remove(Me)
562        End If
563        MyBase.OnUnload(e)
564    End Sub
565
566    Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
567        If Me.Visible Then
568            GenerateNewCaptcha()
569        End If
570        MyBase.OnPreRender(e)
571    End Sub
572
573End Class