diff --git a/plugins/captcha/AUTHORS b/plugins/captcha/AUTHORS new file mode 100644 index 0000000..8e3271f --- /dev/null +++ b/plugins/captcha/AUTHORS @@ -0,0 +1,3 @@ +Silvio Clecio +Luciano Souza +Joao Morais \ No newline at end of file diff --git a/plugins/captcha/LICENSE b/plugins/captcha/LICENSE new file mode 100644 index 0000000..23acdf7 --- /dev/null +++ b/plugins/captcha/LICENSE @@ -0,0 +1,28 @@ +Captcha plugin. + +Copyright (C) 2013 Silvio Clecio - silvioprog@gmail.com + +This library is free software; you can redistribute it and/or modify it +under the terms of the GNU Library General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at your +option) any later version with the following modification: + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent modules,and +to copy and distribute the resulting executable under terms of your choice, +provided that you also meet, for each linked independent module, the terms +and conditions of the license of that module. An independent module is a +module which is not derived from or based on this library. If you modify +this library, you may extend this exception to your version of the library, +but you are not obligated to do so. If you do not wish to do so, delete this +exception statement from your version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License +for more details. + +You should have received a copy of the GNU Library General Public License +along with this library; if not, write to the Free Software Foundation, +Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. \ No newline at end of file diff --git a/plugins/captcha/README b/plugins/captcha/README new file mode 100644 index 0000000..168f32f --- /dev/null +++ b/plugins/captcha/README @@ -0,0 +1,19 @@ +Captcha plugin +============== + +Nowadays, Pascal is fully immersed in web development world. Therefore, it faces the severe issue of spams. +This plugin provides the handling of verification images, usually to block the free and automatic access in login pages. + +Installation +============ + +1. Open the package "captchapkg.lpk", in the dialog that opens, click in "Use >>" and "Add to Project"; + +Steps to run this demo +====================== + +On Windows, copy the DLLs "freetype-6.dll" and "zlib1.dll" and the font "LiberationSerif-Regular.ttf" to your project folder. + +On Linux, copy the font "LiberationSerif-Regular.ttf" to your project folder. + +Enjoy! \ No newline at end of file diff --git a/plugins/captcha/demo/brokers.pas b/plugins/captcha/demo/brokers.pas new file mode 100644 index 0000000..f5dcdc7 --- /dev/null +++ b/plugins/captcha/demo/brokers.pas @@ -0,0 +1,12 @@ +unit Brokers; + +{$mode objfpc}{$H+} + +interface + +uses + BrookFCLCGIBroker; + +implementation + +end. diff --git a/plugins/captcha/demo/cgi1.lpi b/plugins/captcha/demo/cgi1.lpi new file mode 100644 index 0000000..7587bb7 --- /dev/null +++ b/plugins/captcha/demo/cgi1.lpi @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/captcha/demo/cgi1.lpr b/plugins/captcha/demo/cgi1.lpr new file mode 100644 index 0000000..d723ead --- /dev/null +++ b/plugins/captcha/demo/cgi1.lpr @@ -0,0 +1,10 @@ +program cgi1; + +{$mode objfpc}{$H+} + +uses + BrookApplication, Brokers, test; + +begin + BrookApp.Run; +end. diff --git a/plugins/captcha/demo/test.pas b/plugins/captcha/demo/test.pas new file mode 100644 index 0000000..8607638 --- /dev/null +++ b/plugins/captcha/demo/test.pas @@ -0,0 +1,69 @@ +unit test; + +{$mode objfpc}{$H+} + +interface + +uses + BrookAction, Captcha, RUtils, Classes, SysUtils; + +type + TTest = class(TBrookAction) + public + procedure Get; override; + procedure Post; override; + end; + +const + FORM = + ''+lf+ + ''+lf+ + ''+lf+ + ' '+lf+ + ' '+lf+ + ''+lf+ + ''+lf+ + ''+lf+ + '
'+lf+ + ' '+lf+ + ' '+lf+ + '
'+lf+ + ''+lf+ + ''; + +implementation + +uses + FPWritePNG; + +procedure TTest.Get; +var + VImage: TMemoryImage; + VStream: TMemoryStream; +begin + if not TCaptcha.FontExists(ftLiberationSerifRegularTTF) then + Exit; + VImage := TMemoryImage.Create; + VStream := TMemoryStream.Create; + try + SetCookie('captcha', TCaptcha.Generate(VStream)); + VStream.Seek(0, 0); + Write(FORM, [StreamToBase64(VStream)]); + finally + VStream.Free; + VImage.Free; + end; +end; + +procedure TTest.Post; +begin + if SameText(Fields.Values['captcha'], GetCookie('captcha')) then + Write('OK') + else + Write('Fail'); +end; + +initialization + TTest.Register('*'); + +end. diff --git a/plugins/captcha/pkg/captchapkg.lpk b/plugins/captcha/pkg/captchapkg.lpk new file mode 100644 index 0000000..3e92c8d --- /dev/null +++ b/plugins/captcha/pkg/captchapkg.lpk @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/captcha/src/captcha.pas b/plugins/captcha/src/captcha.pas new file mode 100644 index 0000000..bb67909 --- /dev/null +++ b/plugins/captcha/src/captcha.pas @@ -0,0 +1,183 @@ +(* + Captcha plugin. + Copyright (C) 2012-2014 Silvio Clecio. + + Please see the LICENSE file. +*) + +unit Captcha; + +{$mode objfpc}{$H+} + +interface + +uses + FPCanvas, FPImage, FPImgCanv, FTFont, Classes, SysUtils; + +const + colDkTeal: TFPColor = (Red: $0000; Green: $2000; Blue: $5000; + Alpha: AlphaOpaque); + ftLiberationSerifRegularTTF = 'LiberationSerif-Regular.ttf'; + +type + TCaptchaType = (ctNoise, ctLine); + + { TMemoryImage } + + TMemoryImage = class(TFPMemoryImage) + public + constructor Create; overload; + end; + + { TCaptcha } + + TCaptcha = class + public + class function FontExists(const AFileName: TFileName): Boolean; + class function Generate(AImage: TMemoryImage; + const AFontColor: TFPColor; const AFontName: ShortString = + ftLiberationSerifRegularTTF; const AFontSize: Byte = 18; + const AEasyRead: Byte = 3; const ABackground: Boolean = True; + const ACaptchaType: TCaptchaType = ctLine): string; overload; + class function Generate(AImage: TMemoryImage; + const ACaptchaType: TCaptchaType = ctLine): string; overload; + class function Generate(AStream: TStream; + const ACaptchaType: TCaptchaType = ctLine): string; overload; + end; + +implementation + +{ TMemoryImage } + +constructor TMemoryImage.Create; +begin + inherited Create(100, 100); +end; + +{ TCaptcha } + +class function TCaptcha.FontExists(const AFileName: TFileName): Boolean; +{$IFDEF UNIX} +const + VFontPath = '/usr/share/fonts'; +{$ENDIF} +begin + Result := FileExists(AFileName) or +{$IFDEF UNIX} + (FileExists(IncludeTrailingPathDelimiter(GetEnvironmentVariable('HOME')) + + '.fonts/' + AFileName)) or + (FileExists(VFontPath + AFileName)) or + (FileExists(VFontPath + '/truetype/' + AFileName)) or + (FileExists(VFontPath + '/truetype/freefont/')) ; +{$ENDIF} +{$IFDEF MSWINDOWS} + FileExists(IncludeTrailingPathDelimiter(GetEnvironmentVariable('windir')) + + 'Fonts' + DirectorySeparator + AFileName); +{$ENDIF} +end; + +{$WARNINGS OFF} +class function TCaptcha.Generate(AImage: TMemoryImage; + const AFontColor: TFPColor; const AFontName: ShortString; + const AFontSize: Byte; const AEasyRead: Byte; const ABackground: Boolean; + const ACaptchaType: TCaptchaType): string; + + function _Min(A, B: Integer): Integer; + begin + if A < B then + Result := A + else + Result := B; + end; + + function _RandomRange(const AFrom, ATo: Integer): Integer; + begin + Result := Random(Abs(AFrom - ATo)) + _Min(ATo, AFrom); + end; + +const + NUMBERS = '0123456789'; + ALPHA_NUMERIC = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + NUMBERS; +var + I: Integer; + VFont: TFreeTypeFont; + VCanvas: TFPImageCanvas; +begin + VFont := TFreeTypeFont.Create; + VCanvas := TFPImageCanvas.Create(AImage); + AImage.SetSize(80, 20); + try + VFont.Name := AFontName; + VFont.Size := AFontSize; + VFont.FPColor := AFontColor; + VCanvas.Font := VFont; + if ABackground then + begin + VCanvas.Brush.FPColor := colWhite; + VCanvas.Brush.Style := bsSolid; + VCanvas.FillRect(0, 0, VCanvas.Width, VCanvas.Height); + end; + VCanvas.Clear; + VCanvas.Pen.FPColor := VFont.FPColor; + if ACaptchaType = ctNoise then + begin + for I := 0 to (AImage.Width * AImage.Height) div AEasyRead do + VCanvas.EllipseC(_RandomRange(0, AImage.Width), + _RandomRange(0, AImage.Height), 0, 0); + for I := 1 to 6 do + Result += Copy(NUMBERS, _RandomRange(0, Length(NUMBERS) - 1), 1); + VCanvas.TextOut((VCanvas.Width div 2) - + (VCanvas.GetTextWidth(Result) div 2), + (VCanvas.Height div 2) + (VCanvas.GetTextHeight(Result) div 2), + Result); + end + else + begin + for I := 0 to 3 do + Result += ALPHA_NUMERIC[Random(Length(ALPHA_NUMERIC) - 1) + 1]; + for I := 0 to 3 do + begin + VCanvas.Font.Size := _RandomRange(4, 12) + 6; + VCanvas.TextOut(I * 22, 16, Result[I + 1]); + end; + for I := 0 to AEasyRead do + begin + VCanvas.MoveTo(Random(AImage.Width), 0); + VCanvas.LineTo(Random(AImage.Width), AImage.Height); + end; + end; + finally + VFont.Free; + VCanvas.Free; + end; +end; +{$WARNINGS ON} + +class function TCaptcha.Generate(AImage: TMemoryImage; + const ACaptchaType: TCaptchaType): string; +begin + Result := Generate(AImage, colDkTeal, ftLiberationSerifRegularTTF, 18, 3, + True, ACaptchaType); +end; + +class function TCaptcha.Generate(AStream: TStream; + const ACaptchaType: TCaptchaType): string; +var + VImage: TMemoryImage; + VWriter: TFPCustomImageWriter; +begin + VImage := TMemoryImage.Create; + VWriter := ImageHandlers.ImageWriter[ImageHandlers.TypeNames[0]].Create; + try + Result := Generate(VImage, ACaptchaType); + VImage.SaveToStream(AStream, VWriter); + finally + VImage.Free; + VWriter.Free; + end; +end; + +initialization + Randomize; + +end.