IntroductionThe word ‘CAPTCHA’ stands for Completely Automated Public Turing test to tell Computers and Humans Apart. Most World Wide Web users will have seen this kind of test in the form of a picture of a word (usually distorted), which the user must type into an input box to prove that they are a real person, and not just a spambot, or some other computerized agent trawling the Web for exploits. A CAPTCHA is a challenge-response test most often placed within web forms to determine whether the user is human. The purpose of CAPTCHA is to block form submissions from spambots – automated scripts that harvest email address from publicly available web forms.Implementing CAPTCHA For SharePointCreate HTTPHandler for Generating ImageThe easiest way to create a custom HttpHandler component is to create a source file with an .ashx extension. You must then add a @WebHandler directive to the top of the .ashx file, along with a class definition that implements the IHttpHandler interface. Any class that implements the IHttpHandler interface must provide an implementation of the IsReusable method and the ProcessRequest method. If you want to be able to program against the Windows SharePoint Services object model from inside the HttpHandler component, you can also add an @Assembly directive to reference the Microsoft.SharePoint assembly.1. Create a folder inside LAYOUTS directory. For e.g.: C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS\Sprint2. Create a file named captcha.ashx inside that folder.3. Include the following code in captcha.ashx
<%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ WebHandler Language="C#" Class="captcha" %>
using System;
using System.Web;
using System.Drawing;
using System.Web.SessionState;
using System.Data;
using System.Configuration;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Drawing.Drawing2D;
public class captcha : IHttpHandler, IRequiresSessionState
{
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "image/jpeg";
ADSSAntiBot captcha = new ADSSAntiBot();
string str = captcha.DrawNumbers(5);
if (context.Session[ ADSSAntiBot.SESSION_CAPTCHA] == null) context.Session.Add(ADSSAntiBot.SESSION_CAPTCHA, str);
else
{
context.Session[ ADSSAntiBot.SESSION_CAPTCHA] = str;
}
Bitmap bmp = captcha.Result;
bmp.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
}
public bool IsReusable {
get {
return true;
}
}
}
public class ADSSAntiBot
{
public static string SESSION_CAPTCHA = "CAPTCHA";
const int default_width = 135;
const int default_height = 35;
protected Bitmap result = null;
public int Width;
public int Height;
public ADSSAntiBot()
{
InitBitmap(default_width, default_height);
rnd = new Random();
}
public ADSSAntiBot(int width, int height)
{
InitBitmap(width, height);
}
protected void InitBitmap(int width, int height)
{
result = new Bitmap(width, height);
Width = width;
Height = height;
rnd = new Random();
}
public PointF Noise(PointF p, double eps)
{
p.X = Convert.ToSingle(rnd.NextDouble() * eps * 2 - eps) + p.X;
p.Y = Convert.ToSingle(rnd.NextDouble() * eps * 2 - eps) + p.Y;
return p;
}
public PointF Wave(PointF p, double amp, double size)
{
p.Y = Convert.ToSingle(Math.Sin(p.X / size) * amp) + p.Y;
p.X = Convert.ToSingle(Math.Sin(p.X / size) * amp) + p.X;
return p;
}
public GraphicsPath RandomWarp(GraphicsPath path)
{
// Add line //
int PsCount = 10;
PointF[] curvePs = new PointF[PsCount * 2];
for (int u = 0; u < PsCount; u++)
{
curvePs[u].X = u * (Width / PsCount);
curvePs[u].Y = Height / 2;
}
for (int u = PsCount; u < (PsCount * 2); u++)
{
curvePs[u].X = (u - PsCount) * (Width / PsCount);
curvePs[u].Y = Height / 2 + 2;
}
path.AddLines(curvePs);
//
double eps = Height * 0.05;
double amp = rnd.NextDouble() * (double)(Height / 3);
double size = rnd.NextDouble() * (double)(Width / 4) + Width / 8;
double offset = (double)(Height / 3);
PointF[] pn = new PointF[path.PointCount];
byte[] pt = new byte[path.PointCount];
GraphicsPath np2 = new GraphicsPath();
GraphicsPathIterator iter = new GraphicsPathIterator(path);
for (int i = 0; i < iter.SubpathCount; i++)
{
GraphicsPath sp = new GraphicsPath();
bool closed;
iter.NextSubpath(sp, out closed);
Matrix m = new Matrix();
m.RotateAt(Convert.ToSingle(rnd.NextDouble() * 30 - 15), sp.PathPoints[0]);
//m.Shear(Convert.ToSingle( rnd.NextDouble()*offset-offset ),Convert.ToSingle( rnd.NextDouble()*offset-offset/2 ));
//m.Shear(1,1);
//m.Scale(0.5f + Convert.ToSingle(rnd.NextDouble()), 0.5f + Convert.ToSingle(rnd.NextDouble()), MatrixOrder.Prepend);
m.Translate(-1 * i, 0);
sp.Transform(m);
np2.AddPath(sp, true);
}
for (int i = 0; i < np2.PointCount; i++)
{
//pn[i] = Noise( path.PathPoints[i] , eps);
pn[i] = Wave(np2.PathPoints[i], amp, size);
pt[i] = np2.PathTypes[i];
}
GraphicsPath newpath = new GraphicsPath(pn, pt);
return newpath;
}
Random rnd;
public string DrawNumbers(int len)
{
string str = "";
for (int i = 0; i < len; i++)
{
int n = rnd.Next() % 10;
str += n.ToString();
}
DrawText(str);
return str;
}
public void DrawText(string aText)
{
Graphics g = Graphics.FromImage(result);
int startsize = Height;
Font f = new Font("Verdana", startsize, FontStyle.Bold, GraphicsUnit.Pixel);
do
{
f = new Font("Verdana", startsize, GraphicsUnit.Pixel);
startsize--;
} while ((g.MeasureString(aText, f).Width >= Width) || (g.MeasureString(aText, f).Height >= Height));
SizeF sf = g.MeasureString(aText, f);
int width = Convert.ToInt32(sf.Width);
int height = Convert.ToInt32(sf.Height);
int x = Convert.ToInt32(Math.Abs((double)width - (double)Width) * rnd.NextDouble());
int y = Convert.ToInt32(Math.Abs((double)height - (double)Height) * rnd.NextDouble());
//////// Paths ///
GraphicsPath path = new GraphicsPath(FillMode.Alternate);
FontFamily family = new FontFamily("Verdana");
int fontStyle = (int)(FontStyle.Regular);
float emSize = f.Size;
Point origin = new Point(x, y);
StringFormat format = StringFormat.GenericDefault;
path.AddString(aText, family, fontStyle, emSize, origin, format);
path = RandomWarp(path);
/// Path ///
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
Rectangle rect = new Rectangle(0, 0, Width, Height);
g.FillRectangle(new System.Drawing.Drawing2D.LinearGradientBrush(rect, Color.White, Color.LightGray, 0f), rect);
//g.DrawString(aText, f, new SolidBrush(Color.Black), x, y);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.FillPath(new SolidBrush(Color.Black), path);
// Dispose //
g.Dispose();
}
public Bitmap Result
{
get
{
return result;
}
}
}
Now, this HTTPHandler is accessible to any site in the farm by using a site-relative path. http://MyWebServer/_layouts/ACME/captcha.ashx
Note:Please make sure that you have unblocked .ashx extension for your web application from Central Administration.
Unblock .ashx extension for your web application from Central Administration
1. Open SharePoint 3.0 Central Administration.
2. Click Operations tab.
3. Click Blocked file types under Security Configuration.
4. Select your web application.
5. Remove ashx file extension from the list.
6. Click OK.
Implement SharePoint WebPart for CAPTCHA
1. Create a Class Library Project using Visual Studio 2005.
2. Add a reference to:
a. System.Web
b. Microsoft.SharePoint
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebPartPages;
namespace CaptchaWebPart
{
public class CaptchaWebPart : WebPart
{
private Image imgCaptcha;
private TextBox txtCaptchaNumber;
private RequiredFieldValidator rfv;
private CustomValidator captchaValidator; // Performs user-defined validation on an input control.
///
/// Override the ASP.NET Web.UI.Controls.CreateChildControls method
/// to create the objects for the Web Part's controls.
///
protected override void CreateChildControls()
{
// Get the SPWeb
//SPSite siteColl = SPContext.Current.Site;
SPWeb site = SPContext.Current.Web;
// table Formatting
Controls.Add(new LiteralControl("<_table border="\">"));
Controls.Add(new LiteralControl("<_tr><_td>"));
// CAPTCHA Image
imgCaptcha = new Image();
imgCaptcha.ID = "imgCaptcha";
imgCaptcha.ImageUrl = site.Url + "/_layouts/Sprint/captcha.ashx";
imgCaptcha.AlternateText = "If you can't read this number refresh your screen.";
Controls.Add(imgCaptcha);
Controls.Add(new LiteralControl(""));
Controls.Add(new LiteralControl("<_tr><_td>"));
Controls.Add(new LiteralControl("<_strong>Enter the code shown above: <_span style="\">*<_br>"));
// Textbox for user to enter CAPTCHA Text
txtCaptchaNumber = new TextBox();
txtCaptchaNumber.ID = "txtCaptchaNumber";
txtCaptchaNumber.EnableViewState = false;
Controls.Add(txtCaptchaNumber);
// Required Field Validator
rfv = new RequiredFieldValidator();
rfv.ControlToValidate = "txtCaptchaNumber";
rfv.Display = ValidatorDisplay.Dynamic;
rfv.Text = "*";
Controls.Add(rfv);
captchaValidator = new CustomValidator();
captchaValidator.ServerValidate += new ServerValidateEventHandler(captchaValidator_ServerValidate);
captchaValidator.Display = ValidatorDisplay.Dynamic;
captchaValidator.Text = "Incorrect Code";
Controls.Add(captchaValidator);
Controls.Add(new LiteralControl("<_br><_i>(Note: If you cannot read the numbers in the above image, reload the page to generate a new one.)"));
Controls.Add(new LiteralControl(""));
Controls.Add(new LiteralControl(""));
}
///
/// Renders the HTML for the body of a Web Part to the client.
///
///
protected override void RenderWebPart(HtmlTextWriter output)
{
RenderChildren(output);
}
///
/// Raises the ServerValidate event for the CustomValidator control.
///
///
///
void captchaValidator_ServerValidate(object source, ServerValidateEventArgs args)
{
if (txtCaptchaNumber.Text == (string)this.Page.Session["CAPTCHA"])
{
args.IsValid = true;
}
else
{
args.IsValid = false;
}
}
}
}
Note: Please remove _ from above code appearing before HTML Tags (for e.g.: _table, _td, _tr)
3. Sign the assembly with Strong Key Name file.4. Compile the code.Configuring Portal to use Captcha WebPart
1. Navigate to Blog’s site.2. Click on Comments link under any blog post. (http://provpc:24106/ACME/Lists/Posts/Post.aspx?ID=1#Comments&PageView=Shared)3. Click Site Actions -> Edit Page.4. Click Add a Web Part.5. Select CaptchaWebPart from category Miscellaneous under All Web Parts.6. Click edit -> Modify Shared Web Part.7. Select Chrome Type as None.8. Click OK.9. Align CaptchaWebPart below New Comment WebPart.10.Click Exit Edit Mode.The CAPTCHA WebPart will appear as below:Code Download: