Captcha and design works.
Added captcha generation. As we're working without JS - there is no possibility to reload image, only if user will reload whole web page. Some design works - set monospace font for paste textarea (will use system-defined default monospace font) and lowered font size in paste views.
This commit is contained in:
		
							
								
								
									
										8
									
								
								Gopkg.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								Gopkg.lock
									
									
									
										generated
									
									
									
								
							| @@ -44,6 +44,12 @@ | ||||
|   packages = ["."] | ||||
|   revision = "cbb64ac3d964b81592e64f957ad53df015803288" | ||||
|  | ||||
| [[projects]] | ||||
|   branch = "master" | ||||
|   name = "github.com/dchest/captcha" | ||||
|   packages = ["."] | ||||
|   revision = "6a29415a8364ec2971fdc62d9e415ed53fc20410" | ||||
|  | ||||
| [[projects]] | ||||
|   name = "github.com/dgrijalva/jwt-go" | ||||
|   packages = ["."] | ||||
| @@ -174,6 +180,6 @@ | ||||
| [solve-meta] | ||||
|   analyzer-name = "dep" | ||||
|   analyzer-version = 1 | ||||
|   inputs-digest = "0e162c63d4eebc3e71a0d1ebba784963b8aa60886a706e396bc9d35d7d12802f" | ||||
|   inputs-digest = "2633c4577a85d689bec7db65efe6c7f4da249d49bad8bee68df1eb0ab037bdef" | ||||
|   solver-name = "gps-cdcl" | ||||
|   solver-version = 1 | ||||
|   | ||||
| @@ -22,7 +22,7 @@ whistles, no websockets and even NO JAVASCRIPT!(*) | ||||
| Just issue: | ||||
|  | ||||
| ``` | ||||
| go get -u -v github.com/pztrn/fastpastebin | ||||
| go get -u -v github.com/pztrn/fastpastebin/cmd/fastpastebin | ||||
| ``` | ||||
|  | ||||
| This command can be used to update Fast Paste Bin. | ||||
|   | ||||
| @@ -31,6 +31,7 @@ import ( | ||||
|  | ||||
| 	// local | ||||
| 	"github.com/pztrn/fastpastebin/api/http/static" | ||||
| 	"github.com/pztrn/fastpastebin/captcha" | ||||
|  | ||||
| 	// other | ||||
| 	"github.com/alecthomas/chroma/lexers" | ||||
| @@ -54,5 +55,9 @@ func indexGet(ec echo.Context) error { | ||||
|  | ||||
| 	html := strings.Replace(string(htmlRaw), "{lexers}", availableLexersSelectOpts, 1) | ||||
|  | ||||
| 	// Captcha. | ||||
| 	captchaString := captcha.NewCaptcha() | ||||
| 	html = strings.Replace(html, "{captchaString}", captchaString, -1) | ||||
|  | ||||
| 	return ec.HTML(http.StatusOK, html) | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| // Code generated by fileb0x at "2018-05-01 14:28:30.814103749 +0500 +05 m=+0.033880643" from config file "fileb0x.yml" DO NOT EDIT. | ||||
| // modification hash(173f89ae699d558787ec5f8317fe49c2.5c3b355fe5a9e5324e826569a62de332) | ||||
| // Code generated by fileb0x at "2018-05-01 17:43:41.900415103 +0500 +05 m=+0.037136047" from config file "fileb0x.yml" DO NOT EDIT. | ||||
| // modification hash(6b14c8cdfc1bdcdaa0abe70393080151.5c3b355fe5a9e5324e826569a62de332) | ||||
|  | ||||
| package static | ||||
|  | ||||
|   | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -1,5 +1,5 @@ | ||||
| // Code generaTed by fileb0x at "2018-05-01 14:28:30.817594806 +0500 +05 m=+0.037371712" from config file "fileb0x.yml" DO NOT EDIT. | ||||
| // modified(2018-05-01 14:28:26.665088437 +0500 +05) | ||||
| // Code generaTed by fileb0x at "2018-05-01 17:43:21.659447306 +0500 +05 m=+0.033177939" from config file "fileb0x.yml" DO NOT EDIT. | ||||
| // modified(2018-05-01 17:43:05.438136251 +0500 +05) | ||||
| // original path: assets/paste.html | ||||
|  | ||||
| package static | ||||
| @@ -10,7 +10,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| // FilePasteHTML is "/paste.html" | ||||
| var FilePasteHTML = []byte("\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\x68\x74\x6d\x6c\x3e\x0a\x3c\x68\x74\x6d\x6c\x3e\x0a\x0a\x3c\x68\x65\x61\x64\x3e\x0a\x20\x20\x20\x20\x3c\x6d\x65\x74\x61\x20\x63\x68\x61\x72\x73\x65\x74\x3d\x22\x75\x74\x66\x2d\x38\x22\x3e\x0a\x20\x20\x20\x20\x3c\x6d\x65\x74\x61\x20\x6e\x61\x6d\x65\x3d\x22\x76\x69\x65\x77\x70\x6f\x72\x74\x22\x20\x63\x6f\x6e\x74\x65\x6e\x74\x3d\x22\x77\x69\x64\x74\x68\x3d\x64\x65\x76\x69\x63\x65\x2d\x77\x69\x64\x74\x68\x2c\x20\x69\x6e\x69\x74\x69\x61\x6c\x2d\x73\x63\x61\x6c\x65\x3d\x31\x22\x3e\x0a\x20\x20\x20\x20\x3c\x74\x69\x74\x6c\x65\x3e\x46\x61\x73\x74\x20\x50\x61\x73\x74\x65\x20\x42\x69\x6e\x20\x2d\x20\x45\x52\x52\x4f\x52\x21\x3c\x2f\x74\x69\x74\x6c\x65\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x73\x74\x79\x6c\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x63\x73\x73\x2f\x62\x75\x6c\x6d\x61\x2d\x30\x2e\x37\x2e\x30\x2e\x6d\x69\x6e\x2e\x63\x73\x73\x22\x3e\x0a\x20\x20\x20\x20\x3c\x73\x63\x72\x69\x70\x74\x20\x64\x65\x66\x65\x72\x20\x73\x72\x63\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x6a\x73\x2f\x66\x6f\x6e\x74\x61\x77\x65\x73\x6f\x6d\x65\x2d\x35\x2e\x30\x2e\x37\x2e\x6a\x73\x22\x3e\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x73\x74\x79\x6c\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x63\x73\x73\x2f\x73\x74\x79\x6c\x65\x2e\x63\x73\x73\x22\x3e\x0a\x3c\x2f\x68\x65\x61\x64\x3e\x0a\x0a\x3c\x62\x6f\x64\x79\x3e\x0a\x20\x20\x20\x20\x3c\x6e\x61\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x20\x69\x73\x2d\x64\x61\x72\x6b\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x62\x72\x61\x6e\x64\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x69\x74\x65\x6d\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x22\x3e\x46\x61\x73\x74\x20\x50\x61\x73\x74\x65\x20\x42\x69\x6e\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x62\x75\x72\x67\x65\x72\x20\x62\x75\x72\x67\x65\x72\x22\x20\x64\x61\x74\x61\x2d\x74\x61\x72\x67\x65\x74\x3d\x22\x6e\x61\x76\x62\x61\x72\x45\x78\x61\x6d\x70\x6c\x65\x54\x72\x61\x6e\x73\x70\x61\x72\x65\x6e\x74\x45\x78\x61\x6d\x70\x6c\x65\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x70\x61\x6e\x3e\x3c\x2f\x73\x70\x61\x6e\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x70\x61\x6e\x3e\x3c\x2f\x73\x70\x61\x6e\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x70\x61\x6e\x3e\x3c\x2f\x73\x70\x61\x6e\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x69\x64\x3d\x22\x6e\x61\x76\x62\x61\x72\x45\x78\x61\x6d\x70\x6c\x65\x54\x72\x61\x6e\x73\x70\x61\x72\x65\x6e\x74\x45\x78\x61\x6d\x70\x6c\x65\x22\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x6d\x65\x6e\x75\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x73\x74\x61\x72\x74\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x69\x74\x65\x6d\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x70\x61\x73\x74\x65\x73\x2f\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x50\x61\x73\x74\x65\x73\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x65\x6e\x64\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x6e\x61\x76\x3e\x0a\x20\x20\x20\x20\x3c\x73\x65\x63\x74\x69\x6f\x6e\x20\x63\x6c\x61\x73\x73\x3d\x22\x73\x65\x63\x74\x69\x6f\x6e\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x70\x61\x73\x74\x65\x64\x61\x74\x61\x7d\x0a\x20\x20\x20\x20\x3c\x2f\x73\x65\x63\x74\x69\x6f\x6e\x3e\x0a\x3c\x2f\x62\x6f\x64\x79\x3e\x0a\x0a\x3c\x2f\x68\x74\x6d\x6c\x3e") | ||||
| var FilePasteHTML = []byte("\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\x68\x74\x6d\x6c\x3e\x0a\x3c\x68\x74\x6d\x6c\x3e\x0a\x0a\x3c\x68\x65\x61\x64\x3e\x0a\x20\x20\x20\x20\x3c\x6d\x65\x74\x61\x20\x63\x68\x61\x72\x73\x65\x74\x3d\x22\x75\x74\x66\x2d\x38\x22\x3e\x0a\x20\x20\x20\x20\x3c\x6d\x65\x74\x61\x20\x6e\x61\x6d\x65\x3d\x22\x76\x69\x65\x77\x70\x6f\x72\x74\x22\x20\x63\x6f\x6e\x74\x65\x6e\x74\x3d\x22\x77\x69\x64\x74\x68\x3d\x64\x65\x76\x69\x63\x65\x2d\x77\x69\x64\x74\x68\x2c\x20\x69\x6e\x69\x74\x69\x61\x6c\x2d\x73\x63\x61\x6c\x65\x3d\x31\x22\x3e\x0a\x20\x20\x20\x20\x3c\x74\x69\x74\x6c\x65\x3e\x46\x61\x73\x74\x20\x50\x61\x73\x74\x65\x20\x42\x69\x6e\x20\x2d\x20\x45\x52\x52\x4f\x52\x21\x3c\x2f\x74\x69\x74\x6c\x65\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x73\x74\x79\x6c\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x63\x73\x73\x2f\x62\x75\x6c\x6d\x61\x2d\x30\x2e\x37\x2e\x30\x2e\x6d\x69\x6e\x2e\x63\x73\x73\x22\x3e\x0a\x20\x20\x20\x20\x3c\x73\x63\x72\x69\x70\x74\x20\x64\x65\x66\x65\x72\x20\x73\x72\x63\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x6a\x73\x2f\x66\x6f\x6e\x74\x61\x77\x65\x73\x6f\x6d\x65\x2d\x35\x2e\x30\x2e\x37\x2e\x6a\x73\x22\x3e\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x73\x74\x79\x6c\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x63\x73\x73\x2f\x73\x74\x79\x6c\x65\x2e\x63\x73\x73\x22\x3e\x0a\x3c\x2f\x68\x65\x61\x64\x3e\x0a\x0a\x3c\x62\x6f\x64\x79\x3e\x0a\x20\x20\x20\x20\x3c\x6e\x61\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x20\x69\x73\x2d\x64\x61\x72\x6b\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x62\x72\x61\x6e\x64\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x69\x74\x65\x6d\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x22\x3e\x46\x61\x73\x74\x20\x50\x61\x73\x74\x65\x20\x42\x69\x6e\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x62\x75\x72\x67\x65\x72\x20\x62\x75\x72\x67\x65\x72\x22\x20\x64\x61\x74\x61\x2d\x74\x61\x72\x67\x65\x74\x3d\x22\x6e\x61\x76\x62\x61\x72\x45\x78\x61\x6d\x70\x6c\x65\x54\x72\x61\x6e\x73\x70\x61\x72\x65\x6e\x74\x45\x78\x61\x6d\x70\x6c\x65\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x70\x61\x6e\x3e\x3c\x2f\x73\x70\x61\x6e\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x70\x61\x6e\x3e\x3c\x2f\x73\x70\x61\x6e\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x70\x61\x6e\x3e\x3c\x2f\x73\x70\x61\x6e\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x69\x64\x3d\x22\x6e\x61\x76\x62\x61\x72\x45\x78\x61\x6d\x70\x6c\x65\x54\x72\x61\x6e\x73\x70\x61\x72\x65\x6e\x74\x45\x78\x61\x6d\x70\x6c\x65\x22\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x6d\x65\x6e\x75\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x73\x74\x61\x72\x74\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x69\x74\x65\x6d\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x70\x61\x73\x74\x65\x73\x2f\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x50\x61\x73\x74\x65\x73\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x65\x6e\x64\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x6e\x61\x76\x3e\x0a\x20\x20\x20\x20\x3c\x73\x65\x63\x74\x69\x6f\x6e\x20\x63\x6c\x61\x73\x73\x3d\x22\x73\x65\x63\x74\x69\x6f\x6e\x20\x70\x61\x73\x74\x65\x2d\x64\x61\x74\x61\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x70\x61\x73\x74\x65\x64\x61\x74\x61\x7d\x0a\x20\x20\x20\x20\x3c\x2f\x73\x65\x63\x74\x69\x6f\x6e\x3e\x0a\x3c\x2f\x62\x6f\x64\x79\x3e\x0a\x0a\x3c\x2f\x68\x74\x6d\x6c\x3e") | ||||
|  | ||||
| func init() { | ||||
|    | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| // Code generaTed by fileb0x at "2018-04-30 12:45:53.117322185 +0500 +05 m=+0.017381722" from config file "fileb0x.yml" DO NOT EDIT. | ||||
| // modified(2018-04-30 12:45:50.434017895 +0500 +05) | ||||
| // Code generaTed by fileb0x at "2018-05-01 17:43:41.903107504 +0500 +05 m=+0.039828393" from config file "fileb0x.yml" DO NOT EDIT. | ||||
| // modified(2018-05-01 17:43:40.11480207 +0500 +05) | ||||
| // original path: assets/css/style.css | ||||
|  | ||||
| package static | ||||
| @@ -10,7 +10,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| // FileStaticCSSStyleCSS is "static/css/style.css" | ||||
| var FileStaticCSSStyleCSS = []byte("\x23\x70\x61\x73\x74\x65\x2d\x63\x6f\x6e\x74\x65\x6e\x74\x73\x20\x7b\x0a\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3a\x20\x39\x30\x76\x68\x3b\x0a\x7d") | ||||
| var FileStaticCSSStyleCSS = []byte("\x23\x70\x61\x73\x74\x65\x2d\x63\x6f\x6e\x74\x65\x6e\x74\x73\x20\x7b\x0a\x20\x20\x20\x20\x66\x6f\x6e\x74\x2d\x66\x61\x6d\x69\x6c\x79\x3a\x20\x6d\x6f\x6e\x6f\x73\x70\x61\x63\x65\x3b\x0a\x20\x20\x20\x20\x66\x6f\x6e\x74\x2d\x73\x69\x7a\x65\x3a\x20\x30\x2e\x39\x72\x65\x6d\x3b\x0a\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3a\x20\x39\x30\x76\x68\x3b\x0a\x7d\x0a\x0a\x2e\x70\x61\x73\x74\x65\x2d\x64\x61\x74\x61\x20\x7b\x0a\x20\x20\x20\x20\x66\x6f\x6e\x74\x2d\x73\x69\x7a\x65\x3a\x20\x30\x2e\x39\x72\x65\x6d\x3b\x0a\x7d") | ||||
|  | ||||
| func init() { | ||||
|    | ||||
|   | ||||
| @@ -1,3 +1,9 @@ | ||||
| #paste-contents { | ||||
|     font-family: monospace; | ||||
|     font-size: 0.9rem; | ||||
|     height: 90vh; | ||||
| } | ||||
|  | ||||
| .paste-data { | ||||
|     font-size: 0.9rem; | ||||
| } | ||||
| @@ -82,6 +82,16 @@ | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                     <div class="field"> | ||||
|                         <label for="paste-captcha">Captcha:</label> | ||||
|                         <div class="control"> | ||||
|                             <p> | ||||
|                                 <img id=image src="/captcha/{captchaString}.png" alt="Captcha image"> | ||||
|                             </p> | ||||
|                             <input class="input" type="text" placeholder="Captcha solution" name="paste-captcha-solution" id="paste-captcha-solution"> | ||||
|                             <input class="input is-hidden" type="text" name="paste-captcha-id" id="paste-captcha-id" readonly value="{captchaString}"> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                     <div class="field"> | ||||
|                         <div class="control"> | ||||
|                             <input class="button is-success" type="submit" value="Paste!"> | ||||
|   | ||||
| @@ -32,7 +32,7 @@ | ||||
|             </div> | ||||
|         </div> | ||||
|     </nav> | ||||
|     <section class="section"> | ||||
|     <section class="section paste-data"> | ||||
|         {pastedata} | ||||
|     </section> | ||||
| </body> | ||||
|   | ||||
							
								
								
									
										59
									
								
								captcha/exported.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								captcha/exported.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| // Fast Paste Bin - uberfast and easy-to-use pastebin. | ||||
| // | ||||
| // Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin | ||||
| // developers. | ||||
| // | ||||
| // Permission is hereby granted, free of charge, to any person obtaining | ||||
| // a copy of this software and associated documentation files (the | ||||
| // "Software"), to deal in the Software without restriction, including | ||||
| // without limitation the rights to use, copy, modify, merge, publish, | ||||
| // distribute, sublicense, and/or sell copies of the Software, and to | ||||
| // permit persons to whom the Software is furnished to do so, subject | ||||
| // to the following conditions: | ||||
| // | ||||
| // The above copyright notice and this permission notice shall be | ||||
| // included in all copies or substantial portions of the Software. | ||||
| // | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||
| // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||
| // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
| // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||||
| // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||||
| // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | ||||
| // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  | ||||
| package captcha | ||||
|  | ||||
| import ( | ||||
| 	"github.com/labstack/echo" | ||||
| 	// local | ||||
| 	"github.com/pztrn/fastpastebin/context" | ||||
|  | ||||
| 	// other | ||||
| 	"github.com/dchest/captcha" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	c *context.Context | ||||
| ) | ||||
|  | ||||
| // New initializes captcha package and adds neccessary HTTP and API | ||||
| // endpoints. | ||||
| func New(cc *context.Context) { | ||||
| 	c = cc | ||||
|  | ||||
| 	// New paste. | ||||
| 	c.Echo.GET("/captcha/:id.png", echo.WrapHandler(captcha.Server(captcha.StdWidth, captcha.StdHeight))) | ||||
| } | ||||
|  | ||||
| // NewCaptcha creates new captcha string. | ||||
| func NewCaptcha() string { | ||||
| 	s := captcha.New() | ||||
| 	c.Logger.Debug().Msgf("Created new captcha string: %s", s) | ||||
| 	return s | ||||
| } | ||||
|  | ||||
| // Verify verifies captchas. | ||||
| func Verify(captchaString string, captchaSolution string) bool { | ||||
| 	return captcha.VerifyString(captchaString, captchaSolution) | ||||
| } | ||||
| @@ -32,6 +32,7 @@ import ( | ||||
|  | ||||
| 	// local | ||||
| 	"github.com/pztrn/fastpastebin/api" | ||||
| 	"github.com/pztrn/fastpastebin/captcha" | ||||
| 	"github.com/pztrn/fastpastebin/context" | ||||
| 	"github.com/pztrn/fastpastebin/database" | ||||
| 	"github.com/pztrn/fastpastebin/database/migrations" | ||||
| @@ -59,6 +60,7 @@ func main() { | ||||
| 	api.New(c) | ||||
| 	api.InitializeAPI() | ||||
|  | ||||
| 	captcha.New(c) | ||||
| 	pastes.New(c) | ||||
|  | ||||
| 	// CTRL+C handler. | ||||
|   | ||||
| @@ -36,6 +36,7 @@ import ( | ||||
|  | ||||
| 	// local | ||||
| 	"github.com/pztrn/fastpastebin/api/http/static" | ||||
| 	"github.com/pztrn/fastpastebin/captcha" | ||||
| 	"github.com/pztrn/fastpastebin/pagination" | ||||
|  | ||||
| 	// other | ||||
| @@ -44,6 +45,7 @@ import ( | ||||
| 	htmlfmt "github.com/alecthomas/chroma/formatters/html" | ||||
| 	"github.com/alecthomas/chroma/lexers" | ||||
| 	"github.com/alecthomas/chroma/styles" | ||||
| 	//"github.com/dchest/captcha" | ||||
| 	"github.com/labstack/echo" | ||||
| ) | ||||
|  | ||||
| @@ -140,6 +142,13 @@ func pastePOST(ec echo.Context) error { | ||||
| 		return ec.HTML(http.StatusBadRequest, errhtmlAsString) | ||||
| 	} | ||||
|  | ||||
| 	// Verify captcha. | ||||
| 	if !captcha.Verify(params["paste-captcha-id"][0], params["paste-captcha-solution"][0]) { | ||||
| 		c.Logger.Debug().Msgf("Invalid captcha solution for captcha ID '%s': %s", params["paste-captcha-id"][0], params["paste-captcha-solution"][0]) | ||||
| 		errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Invalid captcha solution.", 1) | ||||
| 		return ec.HTML(http.StatusBadRequest, errhtmlAsString) | ||||
| 	} | ||||
|  | ||||
| 	paste := &Paste{ | ||||
| 		Title:    params["paste-title"][0], | ||||
| 		Data:     params["paste-contents"][0], | ||||
|   | ||||
							
								
								
									
										8
									
								
								vendor/github.com/dchest/captcha/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/dchest/captcha/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| # Generated test captchas | ||||
| capgen/*.png | ||||
| capgen/*.wav | ||||
|  | ||||
| # Programs | ||||
| capgen/capgen | ||||
| cangensounds/cangensounds | ||||
| capexample/capexample | ||||
							
								
								
									
										19
									
								
								vendor/github.com/dchest/captcha/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/dchest/captcha/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| Copyright (c) 2011-2014 Dmitry Chestnykh <dmitry@codingrobots.com> | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
| in the Software without restriction, including without limitation the rights | ||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the Software is | ||||
| furnished to do so, subject to the following conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in | ||||
| all copies or substantial portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
| THE SOFTWARE. | ||||
							
								
								
									
										275
									
								
								vendor/github.com/dchest/captcha/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								vendor/github.com/dchest/captcha/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,275 @@ | ||||
| Package captcha | ||||
| ===================== | ||||
|  | ||||
| 	import "github.com/dchest/captcha" | ||||
|  | ||||
| Package captcha implements generation and verification of image and audio | ||||
| CAPTCHAs. | ||||
|  | ||||
| A captcha solution is the sequence of digits 0-9 with the defined length. | ||||
| There are two captcha representations: image and audio. | ||||
|  | ||||
| An image representation is a PNG-encoded image with the solution printed on | ||||
| it in such a way that makes it hard for computers to solve it using OCR. | ||||
|  | ||||
| An audio representation is a WAVE-encoded (8 kHz unsigned 8-bit) sound with the | ||||
| spoken solution (currently in English, Russian, Chinese, and Japanese). To make | ||||
| it hard for computers to solve audio captcha, the voice that pronounces numbers | ||||
| has random speed and pitch, and there is a randomly generated background noise | ||||
| mixed into the sound. | ||||
|  | ||||
| This package doesn't require external files or libraries to generate captcha | ||||
| representations; it is self-contained. | ||||
|  | ||||
| To make captchas one-time, the package includes a memory storage that stores | ||||
| captcha ids, their solutions, and expiration time. Used captchas are removed | ||||
| from the store immediately after calling Verify or VerifyString, while | ||||
| unused captchas (user loaded a page with captcha, but didn't submit the | ||||
| form) are collected automatically after the predefined expiration time. | ||||
| Developers can also provide custom store (for example, which saves captcha | ||||
| ids and solutions in database) by implementing Store interface and | ||||
| registering the object with SetCustomStore. | ||||
|  | ||||
| Captchas are created by calling New, which returns the captcha id.  Their | ||||
| representations, though, are created on-the-fly by calling WriteImage or | ||||
| WriteAudio functions. Created representations are not stored anywhere, but | ||||
| subsequent calls to these functions with the same id will write the same | ||||
| captcha solution. Reload function will create a new different solution for the | ||||
| provided captcha, allowing users to "reload" captcha if they can't solve the | ||||
| displayed one without reloading the whole page.  Verify and VerifyString are | ||||
| used to verify that the given solution is the right one for the given captcha | ||||
| id. | ||||
|  | ||||
| Server provides an http.Handler which can serve image and audio | ||||
| representations of captchas automatically from the URL. It can also be used | ||||
| to reload captchas.  Refer to Server function documentation for details, or | ||||
| take a look at the example in "capexample" subdirectory. | ||||
|  | ||||
|  | ||||
| Examples | ||||
| -------- | ||||
|  | ||||
|  | ||||
|  | ||||
| [Audio](https://github.com/dchest/captcha/raw/master/capgen/example.wav) | ||||
|  | ||||
|  | ||||
| Constants | ||||
| --------- | ||||
|  | ||||
| ``` go | ||||
| const ( | ||||
|     // Default number of digits in captcha solution. | ||||
|     DefaultLen = 6 | ||||
|     // The number of captchas created that triggers garbage collection used | ||||
|     // by default store. | ||||
|     CollectNum = 100 | ||||
|     // Expiration time of captchas used by default store. | ||||
|     Expiration = 10 * time.Minute | ||||
| ) | ||||
| ``` | ||||
|  | ||||
| ``` go | ||||
| const ( | ||||
|     // Standard width and height of a captcha image. | ||||
|     StdWidth  = 240 | ||||
|     StdHeight = 80 | ||||
| ) | ||||
| ``` | ||||
|  | ||||
|  | ||||
| Variables | ||||
| --------- | ||||
|  | ||||
| ``` go | ||||
| var ( | ||||
|     ErrNotFound = errors.New("captcha: id not found") | ||||
| ) | ||||
| ``` | ||||
|  | ||||
|  | ||||
|  | ||||
| Functions | ||||
| --------- | ||||
|  | ||||
| ### func New | ||||
|  | ||||
| 	func New() string | ||||
| 	 | ||||
| New creates a new captcha with the standard length, saves it in the internal | ||||
| storage and returns its id. | ||||
|  | ||||
| ### func NewLen | ||||
|  | ||||
| 	func NewLen(length int) (id string) | ||||
| 	 | ||||
| NewLen is just like New, but accepts length of a captcha solution as the | ||||
| argument. | ||||
|  | ||||
| ### func RandomDigits | ||||
|  | ||||
| 	func RandomDigits(length int) (b []byte) | ||||
| 	 | ||||
| RandomDigits returns a byte slice of the given length containing | ||||
| pseudorandom numbers in range 0-9. The slice can be used as a captcha | ||||
| solution. | ||||
|  | ||||
| ### func Reload | ||||
|  | ||||
| 	func Reload(id string) bool | ||||
| 	 | ||||
| Reload generates and remembers new digits for the given captcha id.  This | ||||
| function returns false if there is no captcha with the given id. | ||||
|  | ||||
| After calling this function, the image or audio presented to a user must be | ||||
| refreshed to show the new captcha representation (WriteImage and WriteAudio | ||||
| will write the new one). | ||||
|  | ||||
| ### func Server | ||||
|  | ||||
| 	func Server(imgWidth, imgHeight int) http.Handler | ||||
| 	 | ||||
| Server returns a handler that serves HTTP requests with image or | ||||
| audio representations of captchas. Image dimensions are accepted as | ||||
| arguments. The server decides which captcha to serve based on the last URL | ||||
| path component: file name part must contain a captcha id, file extension — | ||||
| its format (PNG or WAV). | ||||
|  | ||||
| For example, for file name "LBm5vMjHDtdUfaWYXiQX.png" it serves an image captcha | ||||
| with id "LBm5vMjHDtdUfaWYXiQX", and for "LBm5vMjHDtdUfaWYXiQX.wav" it serves the | ||||
| same captcha in audio format. | ||||
|  | ||||
| To serve a captcha as a downloadable file, the URL must be constructed in | ||||
| such a way as if the file to serve is in the "download" subdirectory: | ||||
| "/download/LBm5vMjHDtdUfaWYXiQX.wav". | ||||
|  | ||||
| To reload captcha (get a different solution for the same captcha id), append | ||||
| "?reload=x" to URL, where x may be anything (for example, current time or a | ||||
| random number to make browsers refetch an image instead of loading it from | ||||
| cache). | ||||
|  | ||||
| By default, the Server serves audio in English language. To serve audio | ||||
| captcha in one of the other supported languages, append "lang" value, for | ||||
| example, "?lang=ru". | ||||
|  | ||||
| ### func SetCustomStore | ||||
|  | ||||
| 	func SetCustomStore(s Store) | ||||
| 	 | ||||
| SetCustomStore sets custom storage for captchas, replacing the default | ||||
| memory store. This function must be called before generating any captchas. | ||||
|  | ||||
| ### func Verify | ||||
|  | ||||
| 	func Verify(id string, digits []byte) bool | ||||
| 	 | ||||
| Verify returns true if the given digits are the ones that were used to | ||||
| create the given captcha id. | ||||
|  | ||||
| The function deletes the captcha with the given id from the internal | ||||
| storage, so that the same captcha can't be verified anymore. | ||||
|  | ||||
| ### func VerifyString | ||||
|  | ||||
| 	func VerifyString(id string, digits string) bool | ||||
| 	 | ||||
| VerifyString is like Verify, but accepts a string of digits.  It removes | ||||
| spaces and commas from the string, but any other characters, apart from | ||||
| digits and listed above, will cause the function to return false. | ||||
|  | ||||
| ### func WriteAudio | ||||
|  | ||||
| 	func WriteAudio(w io.Writer, id string, lang string) error | ||||
| 	 | ||||
| WriteAudio writes WAV-encoded audio representation of the captcha with the | ||||
| given id and the given language. If there are no sounds for the given | ||||
| language, English is used. | ||||
|  | ||||
| ### func WriteImage | ||||
|  | ||||
| 	func WriteImage(w io.Writer, id string, width, height int) error | ||||
| 	 | ||||
| WriteImage writes PNG-encoded image representation of the captcha with the | ||||
| given id. The image will have the given width and height. | ||||
|  | ||||
|  | ||||
| Types | ||||
| ----- | ||||
|  | ||||
| ``` go | ||||
| type Audio struct { | ||||
|     // contains unexported fields | ||||
| } | ||||
| ``` | ||||
|  | ||||
|  | ||||
| ### func NewAudio | ||||
|  | ||||
| 	func NewAudio(id string, digits []byte, lang string) *Audio | ||||
| 	 | ||||
| NewAudio returns a new audio captcha with the given digits, where each digit | ||||
| must be in range 0-9. Digits are pronounced in the given language. If there | ||||
| are no sounds for the given language, English is used. | ||||
|  | ||||
| Possible values for lang are "en", "ja", "ru", "zh". | ||||
|  | ||||
| ### func (*Audio) EncodedLen | ||||
|  | ||||
| 	func (a *Audio) EncodedLen() int | ||||
| 	 | ||||
| EncodedLen returns the length of WAV-encoded audio captcha. | ||||
|  | ||||
| ### func (*Audio) WriteTo | ||||
|  | ||||
| 	func (a *Audio) WriteTo(w io.Writer) (n int64, err error) | ||||
| 	 | ||||
| WriteTo writes captcha audio in WAVE format into the given io.Writer, and | ||||
| returns the number of bytes written and an error if any. | ||||
|  | ||||
| ``` go | ||||
| type Image struct { | ||||
|     *image.Paletted | ||||
|     // contains unexported fields | ||||
| } | ||||
| ``` | ||||
|  | ||||
|  | ||||
| ### func NewImage | ||||
|  | ||||
| 	func NewImage(id string, digits []byte, width, height int) *Image | ||||
| 	 | ||||
| NewImage returns a new captcha image of the given width and height with the | ||||
| given digits, where each digit must be in range 0-9. | ||||
|  | ||||
| ### func (*Image) WriteTo | ||||
|  | ||||
| 	func (m *Image) WriteTo(w io.Writer) (int64, error) | ||||
| 	 | ||||
| WriteTo writes captcha image in PNG format into the given writer. | ||||
|  | ||||
| ``` go | ||||
| type Store interface { | ||||
|     // Set sets the digits for the captcha id. | ||||
|     Set(id string, digits []byte) | ||||
|  | ||||
|     // Get returns stored digits for the captcha id. Clear indicates | ||||
|     // whether the captcha must be deleted from the store. | ||||
|     Get(id string, clear bool) (digits []byte) | ||||
| } | ||||
| ``` | ||||
|  | ||||
| An object implementing Store interface can be registered with SetCustomStore | ||||
| function to handle storage and retrieval of captcha ids and solutions for | ||||
| them, replacing the default memory store. | ||||
|  | ||||
| It is the responsibility of an object to delete expired and used captchas | ||||
| when necessary (for example, the default memory store collects them in Set | ||||
| method after the certain amount of captchas has been stored.) | ||||
|  | ||||
| ### func NewMemoryStore | ||||
|  | ||||
| 	func NewMemoryStore(collectNum int, expiration time.Duration) Store | ||||
| 	 | ||||
| NewMemoryStore returns a new standard memory store for captchas with the | ||||
| given collection threshold and expiration time in seconds. The returned | ||||
| store must be registered with SetCustomStore to replace the default one. | ||||
							
								
								
									
										232
									
								
								vendor/github.com/dchest/captcha/audio.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										232
									
								
								vendor/github.com/dchest/captcha/audio.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,232 @@ | ||||
| // Copyright 2011-2014 Dmitry Chestnykh. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package captcha | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/binary" | ||||
| 	"io" | ||||
| 	"math" | ||||
| ) | ||||
|  | ||||
| const sampleRate = 8000 // Hz | ||||
|  | ||||
| var endingBeepSound []byte | ||||
|  | ||||
| func init() { | ||||
| 	endingBeepSound = changeSpeed(beepSound, 1.4) | ||||
| } | ||||
|  | ||||
| type Audio struct { | ||||
| 	body        *bytes.Buffer | ||||
| 	digitSounds [][]byte | ||||
| 	rng         siprng | ||||
| } | ||||
|  | ||||
| // NewAudio returns a new audio captcha with the given digits, where each digit | ||||
| // must be in range 0-9. Digits are pronounced in the given language. If there | ||||
| // are no sounds for the given language, English is used. | ||||
| // | ||||
| // Possible values for lang are "en", "ja", "ru", "zh". | ||||
| func NewAudio(id string, digits []byte, lang string) *Audio { | ||||
| 	a := new(Audio) | ||||
|  | ||||
| 	// Initialize PRNG. | ||||
| 	a.rng.Seed(deriveSeed(audioSeedPurpose, id, digits)) | ||||
|  | ||||
| 	if sounds, ok := digitSounds[lang]; ok { | ||||
| 		a.digitSounds = sounds | ||||
| 	} else { | ||||
| 		a.digitSounds = digitSounds["en"] | ||||
| 	} | ||||
| 	numsnd := make([][]byte, len(digits)) | ||||
| 	nsdur := 0 | ||||
| 	for i, n := range digits { | ||||
| 		snd := a.randomizedDigitSound(n) | ||||
| 		nsdur += len(snd) | ||||
| 		numsnd[i] = snd | ||||
| 	} | ||||
| 	// Random intervals between digits (including beginning). | ||||
| 	intervals := make([]int, len(digits)+1) | ||||
| 	intdur := 0 | ||||
| 	for i := range intervals { | ||||
| 		dur := a.rng.Int(sampleRate, sampleRate*3) // 1 to 3 seconds | ||||
| 		intdur += dur | ||||
| 		intervals[i] = dur | ||||
| 	} | ||||
| 	// Generate background sound. | ||||
| 	bg := a.makeBackgroundSound(a.longestDigitSndLen()*len(digits) + intdur) | ||||
| 	// Create buffer and write audio to it. | ||||
| 	sil := makeSilence(sampleRate / 5) | ||||
| 	bufcap := 3*len(beepSound) + 2*len(sil) + len(bg) + len(endingBeepSound) | ||||
| 	a.body = bytes.NewBuffer(make([]byte, 0, bufcap)) | ||||
| 	// Write prelude, three beeps. | ||||
| 	a.body.Write(beepSound) | ||||
| 	a.body.Write(sil) | ||||
| 	a.body.Write(beepSound) | ||||
| 	a.body.Write(sil) | ||||
| 	a.body.Write(beepSound) | ||||
| 	// Write digits. | ||||
| 	pos := intervals[0] | ||||
| 	for i, v := range numsnd { | ||||
| 		mixSound(bg[pos:], v) | ||||
| 		pos += len(v) + intervals[i+1] | ||||
| 	} | ||||
| 	a.body.Write(bg) | ||||
| 	// Write ending (one beep). | ||||
| 	a.body.Write(endingBeepSound) | ||||
| 	return a | ||||
| } | ||||
|  | ||||
| // WriteTo writes captcha audio in WAVE format into the given io.Writer, and | ||||
| // returns the number of bytes written and an error if any. | ||||
| func (a *Audio) WriteTo(w io.Writer) (n int64, err error) { | ||||
| 	// Calculate padded length of PCM chunk data. | ||||
| 	bodyLen := uint32(a.body.Len()) | ||||
| 	paddedBodyLen := bodyLen | ||||
| 	if bodyLen%2 != 0 { | ||||
| 		paddedBodyLen++ | ||||
| 	} | ||||
| 	totalLen := uint32(len(waveHeader)) - 4 + paddedBodyLen | ||||
| 	// Header. | ||||
| 	header := make([]byte, len(waveHeader)+4) // includes 4 bytes for chunk size | ||||
| 	copy(header, waveHeader) | ||||
| 	// Put the length of whole RIFF chunk. | ||||
| 	binary.LittleEndian.PutUint32(header[4:], totalLen) | ||||
| 	// Put the length of WAVE chunk. | ||||
| 	binary.LittleEndian.PutUint32(header[len(waveHeader):], bodyLen) | ||||
| 	// Write header. | ||||
| 	nn, err := w.Write(header) | ||||
| 	n = int64(nn) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	// Write data. | ||||
| 	n, err = a.body.WriteTo(w) | ||||
| 	n += int64(nn) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	// Pad byte if chunk length is odd. | ||||
| 	// (As header has even length, we can check if n is odd, not chunk). | ||||
| 	if bodyLen != paddedBodyLen { | ||||
| 		w.Write([]byte{0}) | ||||
| 		n++ | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // EncodedLen returns the length of WAV-encoded audio captcha. | ||||
| func (a *Audio) EncodedLen() int { | ||||
| 	return len(waveHeader) + 4 + a.body.Len() | ||||
| } | ||||
|  | ||||
| func (a *Audio) makeBackgroundSound(length int) []byte { | ||||
| 	b := a.makeWhiteNoise(length, 4) | ||||
| 	for i := 0; i < length/(sampleRate/10); i++ { | ||||
| 		snd := reversedSound(a.digitSounds[a.rng.Intn(10)]) | ||||
| 		snd = changeSpeed(snd, a.rng.Float(0.8, 1.4)) | ||||
| 		place := a.rng.Intn(len(b) - len(snd)) | ||||
| 		setSoundLevel(snd, a.rng.Float(0.2, 0.5)) | ||||
| 		mixSound(b[place:], snd) | ||||
| 	} | ||||
| 	return b | ||||
| } | ||||
|  | ||||
| func (a *Audio) randomizedDigitSound(n byte) []byte { | ||||
| 	s := a.randomSpeed(a.digitSounds[n]) | ||||
| 	setSoundLevel(s, a.rng.Float(0.75, 1.2)) | ||||
| 	return s | ||||
| } | ||||
|  | ||||
| func (a *Audio) longestDigitSndLen() int { | ||||
| 	n := 0 | ||||
| 	for _, v := range a.digitSounds { | ||||
| 		if n < len(v) { | ||||
| 			n = len(v) | ||||
| 		} | ||||
| 	} | ||||
| 	return n | ||||
| } | ||||
|  | ||||
| func (a *Audio) randomSpeed(b []byte) []byte { | ||||
| 	pitch := a.rng.Float(0.9, 1.2) | ||||
| 	return changeSpeed(b, pitch) | ||||
| } | ||||
|  | ||||
| func (a *Audio) makeWhiteNoise(length int, level uint8) []byte { | ||||
| 	noise := a.rng.Bytes(length) | ||||
| 	adj := 128 - level/2 | ||||
| 	for i, v := range noise { | ||||
| 		v %= level | ||||
| 		v += adj | ||||
| 		noise[i] = v | ||||
| 	} | ||||
| 	return noise | ||||
| } | ||||
|  | ||||
| // mixSound mixes src into dst. Dst must have length equal to or greater than | ||||
| // src length. | ||||
| func mixSound(dst, src []byte) { | ||||
| 	for i, v := range src { | ||||
| 		av := int(v) | ||||
| 		bv := int(dst[i]) | ||||
| 		if av < 128 && bv < 128 { | ||||
| 			dst[i] = byte(av * bv / 128) | ||||
| 		} else { | ||||
| 			dst[i] = byte(2*(av+bv) - av*bv/128 - 256) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func setSoundLevel(a []byte, level float64) { | ||||
| 	for i, v := range a { | ||||
| 		av := float64(v) | ||||
| 		switch { | ||||
| 		case av > 128: | ||||
| 			if av = (av-128)*level + 128; av < 128 { | ||||
| 				av = 128 | ||||
| 			} | ||||
| 		case av < 128: | ||||
| 			if av = 128 - (128-av)*level; av > 128 { | ||||
| 				av = 128 | ||||
| 			} | ||||
| 		default: | ||||
| 			continue | ||||
| 		} | ||||
| 		a[i] = byte(av) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // changeSpeed returns new PCM bytes from the bytes with the speed and pitch | ||||
| // changed to the given value that must be in range [0, x]. | ||||
| func changeSpeed(a []byte, speed float64) []byte { | ||||
| 	b := make([]byte, int(math.Floor(float64(len(a))*speed))) | ||||
| 	var p float64 | ||||
| 	for _, v := range a { | ||||
| 		for i := int(p); i < int(p+speed); i++ { | ||||
| 			b[i] = v | ||||
| 		} | ||||
| 		p += speed | ||||
| 	} | ||||
| 	return b | ||||
| } | ||||
|  | ||||
| func makeSilence(length int) []byte { | ||||
| 	b := make([]byte, length) | ||||
| 	for i := range b { | ||||
| 		b[i] = 128 | ||||
| 	} | ||||
| 	return b | ||||
| } | ||||
|  | ||||
| func reversedSound(a []byte) []byte { | ||||
| 	n := len(a) | ||||
| 	b := make([]byte, n) | ||||
| 	for i, v := range a { | ||||
| 		b[n-1-i] = v | ||||
| 	} | ||||
| 	return b | ||||
| } | ||||
							
								
								
									
										165
									
								
								vendor/github.com/dchest/captcha/captcha.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								vendor/github.com/dchest/captcha/captcha.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,165 @@ | ||||
| // Copyright 2011 Dmitry Chestnykh. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // Package captcha implements generation and verification of image and audio | ||||
| // CAPTCHAs. | ||||
| // | ||||
| // A captcha solution is the sequence of digits 0-9 with the defined length. | ||||
| // There are two captcha representations: image and audio. | ||||
| // | ||||
| // An image representation is a PNG-encoded image with the solution printed on | ||||
| // it in such a way that makes it hard for computers to solve it using OCR. | ||||
| // | ||||
| // An audio representation is a WAVE-encoded (8 kHz unsigned 8-bit) sound with | ||||
| // the spoken solution (currently in English, Russian, Chinese, and Japanese). | ||||
| // To make it hard for computers to solve audio captcha, the voice that | ||||
| // pronounces numbers has random speed and pitch, and there is a randomly | ||||
| // generated background noise mixed into the sound. | ||||
| // | ||||
| // This package doesn't require external files or libraries to generate captcha | ||||
| // representations; it is self-contained. | ||||
| // | ||||
| // To make captchas one-time, the package includes a memory storage that stores | ||||
| // captcha ids, their solutions, and expiration time. Used captchas are removed | ||||
| // from the store immediately after calling Verify or VerifyString, while | ||||
| // unused captchas (user loaded a page with captcha, but didn't submit the | ||||
| // form) are collected automatically after the predefined expiration time. | ||||
| // Developers can also provide custom store (for example, which saves captcha | ||||
| // ids and solutions in database) by implementing Store interface and | ||||
| // registering the object with SetCustomStore. | ||||
| // | ||||
| // Captchas are created by calling New, which returns the captcha id.  Their | ||||
| // representations, though, are created on-the-fly by calling WriteImage or | ||||
| // WriteAudio functions. Created representations are not stored anywhere, but | ||||
| // subsequent calls to these functions with the same id will write the same | ||||
| // captcha solution. Reload function will create a new different solution for | ||||
| // the provided captcha, allowing users to "reload" captcha if they can't solve | ||||
| // the displayed one without reloading the whole page.  Verify and VerifyString | ||||
| // are used to verify that the given solution is the right one for the given | ||||
| // captcha id. | ||||
| // | ||||
| // Server provides an http.Handler which can serve image and audio | ||||
| // representations of captchas automatically from the URL. It can also be used | ||||
| // to reload captchas.  Refer to Server function documentation for details, or | ||||
| // take a look at the example in "capexample" subdirectory. | ||||
| package captcha | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// Default number of digits in captcha solution. | ||||
| 	DefaultLen = 6 | ||||
| 	// The number of captchas created that triggers garbage collection used | ||||
| 	// by default store. | ||||
| 	CollectNum = 100 | ||||
| 	// Expiration time of captchas used by default store. | ||||
| 	Expiration = 10 * time.Minute | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	ErrNotFound = errors.New("captcha: id not found") | ||||
| 	// globalStore is a shared storage for captchas, generated by New function. | ||||
| 	globalStore = NewMemoryStore(CollectNum, Expiration) | ||||
| ) | ||||
|  | ||||
| // SetCustomStore sets custom storage for captchas, replacing the default | ||||
| // memory store. This function must be called before generating any captchas. | ||||
| func SetCustomStore(s Store) { | ||||
| 	globalStore = s | ||||
| } | ||||
|  | ||||
| // New creates a new captcha with the standard length, saves it in the internal | ||||
| // storage and returns its id. | ||||
| func New() string { | ||||
| 	return NewLen(DefaultLen) | ||||
| } | ||||
|  | ||||
| // NewLen is just like New, but accepts length of a captcha solution as the | ||||
| // argument. | ||||
| func NewLen(length int) (id string) { | ||||
| 	id = randomId() | ||||
| 	globalStore.Set(id, RandomDigits(length)) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // Reload generates and remembers new digits for the given captcha id.  This | ||||
| // function returns false if there is no captcha with the given id. | ||||
| // | ||||
| // After calling this function, the image or audio presented to a user must be | ||||
| // refreshed to show the new captcha representation (WriteImage and WriteAudio | ||||
| // will write the new one). | ||||
| func Reload(id string) bool { | ||||
| 	old := globalStore.Get(id, false) | ||||
| 	if old == nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	globalStore.Set(id, RandomDigits(len(old))) | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| // WriteImage writes PNG-encoded image representation of the captcha with the | ||||
| // given id. The image will have the given width and height. | ||||
| func WriteImage(w io.Writer, id string, width, height int) error { | ||||
| 	d := globalStore.Get(id, false) | ||||
| 	if d == nil { | ||||
| 		return ErrNotFound | ||||
| 	} | ||||
| 	_, err := NewImage(id, d, width, height).WriteTo(w) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // WriteAudio writes WAV-encoded audio representation of the captcha with the | ||||
| // given id and the given language. If there are no sounds for the given | ||||
| // language, English is used. | ||||
| func WriteAudio(w io.Writer, id string, lang string) error { | ||||
| 	d := globalStore.Get(id, false) | ||||
| 	if d == nil { | ||||
| 		return ErrNotFound | ||||
| 	} | ||||
| 	_, err := NewAudio(id, d, lang).WriteTo(w) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // Verify returns true if the given digits are the ones that were used to | ||||
| // create the given captcha id. | ||||
| // | ||||
| // The function deletes the captcha with the given id from the internal | ||||
| // storage, so that the same captcha can't be verified anymore. | ||||
| func Verify(id string, digits []byte) bool { | ||||
| 	if digits == nil || len(digits) == 0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	reald := globalStore.Get(id, true) | ||||
| 	if reald == nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	return bytes.Equal(digits, reald) | ||||
| } | ||||
|  | ||||
| // VerifyString is like Verify, but accepts a string of digits.  It removes | ||||
| // spaces and commas from the string, but any other characters, apart from | ||||
| // digits and listed above, will cause the function to return false. | ||||
| func VerifyString(id string, digits string) bool { | ||||
| 	if digits == "" { | ||||
| 		return false | ||||
| 	} | ||||
| 	ns := make([]byte, len(digits)) | ||||
| 	for i := range ns { | ||||
| 		d := digits[i] | ||||
| 		switch { | ||||
| 		case '0' <= d && d <= '9': | ||||
| 			ns[i] = d - '0' | ||||
| 		case d == ' ' || d == ',': | ||||
| 			// ignore | ||||
| 		default: | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return Verify(id, ns) | ||||
| } | ||||
							
								
								
									
										214
									
								
								vendor/github.com/dchest/captcha/font.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								vendor/github.com/dchest/captcha/font.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,214 @@ | ||||
| // Copyright 2011 Dmitry Chestnykh. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package captcha | ||||
|  | ||||
| const ( | ||||
| 	fontWidth  = 11 | ||||
| 	fontHeight = 18 | ||||
| 	blackChar  = 1 | ||||
| ) | ||||
|  | ||||
| var font = [][]byte{ | ||||
| 	{ // 0 | ||||
| 		0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, | ||||
| 		0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||
| 		0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, | ||||
| 		0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, | ||||
| 		1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, | ||||
| 		0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, | ||||
| 		0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, | ||||
| 		0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||
| 		0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, | ||||
| 	}, | ||||
| 	{ // 1 | ||||
| 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||
| 		0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||
| 	}, | ||||
| 	{ // 2 | ||||
| 		0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||
| 		1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, | ||||
| 		0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, | ||||
| 		0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, | ||||
| 		0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, | ||||
| 		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | ||||
| 		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||
| 		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||
| 	}, | ||||
| 	{ // 3 | ||||
| 		0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, | ||||
| 		1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, | ||||
| 		0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, | ||||
| 		1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, | ||||
| 		1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||
| 		0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, | ||||
| 	}, | ||||
| 	{ // 4 | ||||
| 		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, | ||||
| 		0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, | ||||
| 		0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, | ||||
| 		0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, | ||||
| 		0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, | ||||
| 		0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, | ||||
| 		0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||
| 		1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||
| 		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||
| 		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||
| 	}, | ||||
| 	{ // 5 | ||||
| 		0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, | ||||
| 		0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, | ||||
| 		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | ||||
| 		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | ||||
| 		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | ||||
| 		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | ||||
| 		0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, | ||||
| 		0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, | ||||
| 		1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||
| 		0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, | ||||
| 	}, | ||||
| 	{ // 6 | ||||
| 		0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, | ||||
| 		0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, | ||||
| 		0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, | ||||
| 		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | ||||
| 		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | ||||
| 		0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||||
| 		1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, | ||||
| 		1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, | ||||
| 		1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, | ||||
| 		1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, | ||||
| 		0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, | ||||
| 		0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||
| 		0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, | ||||
| 	}, | ||||
| 	{ // 7 | ||||
| 		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||
| 		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||
| 		1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, | ||||
| 		0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, | ||||
| 	}, | ||||
| 	{ // 8 | ||||
| 		0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, | ||||
| 		0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, | ||||
| 		0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, | ||||
| 		0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, | ||||
| 		0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||
| 		0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, | ||||
| 		0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, | ||||
| 		0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, | ||||
| 		1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, | ||||
| 		0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, | ||||
| 		0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, | ||||
| 	}, | ||||
| 	{ // 9 | ||||
| 		0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, | ||||
| 		0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||
| 		0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, | ||||
| 		0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, | ||||
| 		0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, | ||||
| 		0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, | ||||
| 		0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, | ||||
| 		0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, | ||||
| 	}, | ||||
| } | ||||
							
								
								
									
										271
									
								
								vendor/github.com/dchest/captcha/image.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										271
									
								
								vendor/github.com/dchest/captcha/image.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,271 @@ | ||||
| // Copyright 2011-2014 Dmitry Chestnykh. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package captcha | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"image" | ||||
| 	"image/color" | ||||
| 	"image/png" | ||||
| 	"io" | ||||
| 	"math" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// Standard width and height of a captcha image. | ||||
| 	StdWidth  = 240 | ||||
| 	StdHeight = 80 | ||||
| 	// Maximum absolute skew factor of a single digit. | ||||
| 	maxSkew = 0.7 | ||||
| 	// Number of background circles. | ||||
| 	circleCount = 20 | ||||
| ) | ||||
|  | ||||
| type Image struct { | ||||
| 	*image.Paletted | ||||
| 	numWidth  int | ||||
| 	numHeight int | ||||
| 	dotSize   int | ||||
| 	rng       siprng | ||||
| } | ||||
|  | ||||
| // NewImage returns a new captcha image of the given width and height with the | ||||
| // given digits, where each digit must be in range 0-9. | ||||
| func NewImage(id string, digits []byte, width, height int) *Image { | ||||
| 	m := new(Image) | ||||
|  | ||||
| 	// Initialize PRNG. | ||||
| 	m.rng.Seed(deriveSeed(imageSeedPurpose, id, digits)) | ||||
|  | ||||
| 	m.Paletted = image.NewPaletted(image.Rect(0, 0, width, height), m.getRandomPalette()) | ||||
| 	m.calculateSizes(width, height, len(digits)) | ||||
| 	// Randomly position captcha inside the image. | ||||
| 	maxx := width - (m.numWidth+m.dotSize)*len(digits) - m.dotSize | ||||
| 	maxy := height - m.numHeight - m.dotSize*2 | ||||
| 	var border int | ||||
| 	if width > height { | ||||
| 		border = height / 5 | ||||
| 	} else { | ||||
| 		border = width / 5 | ||||
| 	} | ||||
| 	x := m.rng.Int(border, maxx-border) | ||||
| 	y := m.rng.Int(border, maxy-border) | ||||
| 	// Draw digits. | ||||
| 	for _, n := range digits { | ||||
| 		m.drawDigit(font[n], x, y) | ||||
| 		x += m.numWidth + m.dotSize | ||||
| 	} | ||||
| 	// Draw strike-through line. | ||||
| 	m.strikeThrough() | ||||
| 	// Apply wave distortion. | ||||
| 	m.distort(m.rng.Float(5, 10), m.rng.Float(100, 200)) | ||||
| 	// Fill image with random circles. | ||||
| 	m.fillWithCircles(circleCount, m.dotSize) | ||||
| 	return m | ||||
| } | ||||
|  | ||||
| func (m *Image) getRandomPalette() color.Palette { | ||||
| 	p := make([]color.Color, circleCount+1) | ||||
| 	// Transparent color. | ||||
| 	p[0] = color.RGBA{0xFF, 0xFF, 0xFF, 0x00} | ||||
| 	// Primary color. | ||||
| 	prim := color.RGBA{ | ||||
| 		uint8(m.rng.Intn(129)), | ||||
| 		uint8(m.rng.Intn(129)), | ||||
| 		uint8(m.rng.Intn(129)), | ||||
| 		0xFF, | ||||
| 	} | ||||
| 	p[1] = prim | ||||
| 	// Circle colors. | ||||
| 	for i := 2; i <= circleCount; i++ { | ||||
| 		p[i] = m.randomBrightness(prim, 255) | ||||
| 	} | ||||
| 	return p | ||||
| } | ||||
|  | ||||
| // encodedPNG encodes an image to PNG and returns | ||||
| // the result as a byte slice. | ||||
| func (m *Image) encodedPNG() []byte { | ||||
| 	var buf bytes.Buffer | ||||
| 	if err := png.Encode(&buf, m.Paletted); err != nil { | ||||
| 		panic(err.Error()) | ||||
| 	} | ||||
| 	return buf.Bytes() | ||||
| } | ||||
|  | ||||
| // WriteTo writes captcha image in PNG format into the given writer. | ||||
| func (m *Image) WriteTo(w io.Writer) (int64, error) { | ||||
| 	n, err := w.Write(m.encodedPNG()) | ||||
| 	return int64(n), err | ||||
| } | ||||
|  | ||||
| func (m *Image) calculateSizes(width, height, ncount int) { | ||||
| 	// Goal: fit all digits inside the image. | ||||
| 	var border int | ||||
| 	if width > height { | ||||
| 		border = height / 4 | ||||
| 	} else { | ||||
| 		border = width / 4 | ||||
| 	} | ||||
| 	// Convert everything to floats for calculations. | ||||
| 	w := float64(width - border*2) | ||||
| 	h := float64(height - border*2) | ||||
| 	// fw takes into account 1-dot spacing between digits. | ||||
| 	fw := float64(fontWidth + 1) | ||||
| 	fh := float64(fontHeight) | ||||
| 	nc := float64(ncount) | ||||
| 	// Calculate the width of a single digit taking into account only the | ||||
| 	// width of the image. | ||||
| 	nw := w / nc | ||||
| 	// Calculate the height of a digit from this width. | ||||
| 	nh := nw * fh / fw | ||||
| 	// Digit too high? | ||||
| 	if nh > h { | ||||
| 		// Fit digits based on height. | ||||
| 		nh = h | ||||
| 		nw = fw / fh * nh | ||||
| 	} | ||||
| 	// Calculate dot size. | ||||
| 	m.dotSize = int(nh / fh) | ||||
| 	if m.dotSize < 1 { | ||||
| 		m.dotSize = 1 | ||||
| 	} | ||||
| 	// Save everything, making the actual width smaller by 1 dot to account | ||||
| 	// for spacing between digits. | ||||
| 	m.numWidth = int(nw) - m.dotSize | ||||
| 	m.numHeight = int(nh) | ||||
| } | ||||
|  | ||||
| func (m *Image) drawHorizLine(fromX, toX, y int, colorIdx uint8) { | ||||
| 	for x := fromX; x <= toX; x++ { | ||||
| 		m.SetColorIndex(x, y, colorIdx) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (m *Image) drawCircle(x, y, radius int, colorIdx uint8) { | ||||
| 	f := 1 - radius | ||||
| 	dfx := 1 | ||||
| 	dfy := -2 * radius | ||||
| 	xo := 0 | ||||
| 	yo := radius | ||||
|  | ||||
| 	m.SetColorIndex(x, y+radius, colorIdx) | ||||
| 	m.SetColorIndex(x, y-radius, colorIdx) | ||||
| 	m.drawHorizLine(x-radius, x+radius, y, colorIdx) | ||||
|  | ||||
| 	for xo < yo { | ||||
| 		if f >= 0 { | ||||
| 			yo-- | ||||
| 			dfy += 2 | ||||
| 			f += dfy | ||||
| 		} | ||||
| 		xo++ | ||||
| 		dfx += 2 | ||||
| 		f += dfx | ||||
| 		m.drawHorizLine(x-xo, x+xo, y+yo, colorIdx) | ||||
| 		m.drawHorizLine(x-xo, x+xo, y-yo, colorIdx) | ||||
| 		m.drawHorizLine(x-yo, x+yo, y+xo, colorIdx) | ||||
| 		m.drawHorizLine(x-yo, x+yo, y-xo, colorIdx) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (m *Image) fillWithCircles(n, maxradius int) { | ||||
| 	maxx := m.Bounds().Max.X | ||||
| 	maxy := m.Bounds().Max.Y | ||||
| 	for i := 0; i < n; i++ { | ||||
| 		colorIdx := uint8(m.rng.Int(1, circleCount-1)) | ||||
| 		r := m.rng.Int(1, maxradius) | ||||
| 		m.drawCircle(m.rng.Int(r, maxx-r), m.rng.Int(r, maxy-r), r, colorIdx) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (m *Image) strikeThrough() { | ||||
| 	maxx := m.Bounds().Max.X | ||||
| 	maxy := m.Bounds().Max.Y | ||||
| 	y := m.rng.Int(maxy/3, maxy-maxy/3) | ||||
| 	amplitude := m.rng.Float(5, 20) | ||||
| 	period := m.rng.Float(80, 180) | ||||
| 	dx := 2.0 * math.Pi / period | ||||
| 	for x := 0; x < maxx; x++ { | ||||
| 		xo := amplitude * math.Cos(float64(y)*dx) | ||||
| 		yo := amplitude * math.Sin(float64(x)*dx) | ||||
| 		for yn := 0; yn < m.dotSize; yn++ { | ||||
| 			r := m.rng.Int(0, m.dotSize) | ||||
| 			m.drawCircle(x+int(xo), y+int(yo)+(yn*m.dotSize), r/2, 1) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (m *Image) drawDigit(digit []byte, x, y int) { | ||||
| 	skf := m.rng.Float(-maxSkew, maxSkew) | ||||
| 	xs := float64(x) | ||||
| 	r := m.dotSize / 2 | ||||
| 	y += m.rng.Int(-r, r) | ||||
| 	for yo := 0; yo < fontHeight; yo++ { | ||||
| 		for xo := 0; xo < fontWidth; xo++ { | ||||
| 			if digit[yo*fontWidth+xo] != blackChar { | ||||
| 				continue | ||||
| 			} | ||||
| 			m.drawCircle(x+xo*m.dotSize, y+yo*m.dotSize, r, 1) | ||||
| 		} | ||||
| 		xs += skf | ||||
| 		x = int(xs) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (m *Image) distort(amplude float64, period float64) { | ||||
| 	w := m.Bounds().Max.X | ||||
| 	h := m.Bounds().Max.Y | ||||
|  | ||||
| 	oldm := m.Paletted | ||||
| 	newm := image.NewPaletted(image.Rect(0, 0, w, h), oldm.Palette) | ||||
|  | ||||
| 	dx := 2.0 * math.Pi / period | ||||
| 	for x := 0; x < w; x++ { | ||||
| 		for y := 0; y < h; y++ { | ||||
| 			xo := amplude * math.Sin(float64(y)*dx) | ||||
| 			yo := amplude * math.Cos(float64(x)*dx) | ||||
| 			newm.SetColorIndex(x, y, oldm.ColorIndexAt(x+int(xo), y+int(yo))) | ||||
| 		} | ||||
| 	} | ||||
| 	m.Paletted = newm | ||||
| } | ||||
|  | ||||
| func (m *Image) randomBrightness(c color.RGBA, max uint8) color.RGBA { | ||||
| 	minc := min3(c.R, c.G, c.B) | ||||
| 	maxc := max3(c.R, c.G, c.B) | ||||
| 	if maxc > max { | ||||
| 		return c | ||||
| 	} | ||||
| 	n := m.rng.Intn(int(max-maxc)) - int(minc) | ||||
| 	return color.RGBA{ | ||||
| 		uint8(int(c.R) + n), | ||||
| 		uint8(int(c.G) + n), | ||||
| 		uint8(int(c.B) + n), | ||||
| 		uint8(c.A), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func min3(x, y, z uint8) (m uint8) { | ||||
| 	m = x | ||||
| 	if y < m { | ||||
| 		m = y | ||||
| 	} | ||||
| 	if z < m { | ||||
| 		m = z | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func max3(x, y, z uint8) (m uint8) { | ||||
| 	m = x | ||||
| 	if y > m { | ||||
| 		m = y | ||||
| 	} | ||||
| 	if z > m { | ||||
| 		m = z | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										108
									
								
								vendor/github.com/dchest/captcha/random.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								vendor/github.com/dchest/captcha/random.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| // Copyright 2011-2014 Dmitry Chestnykh. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package captcha | ||||
|  | ||||
| import ( | ||||
| 	"crypto/hmac" | ||||
| 	"crypto/rand" | ||||
| 	"crypto/sha256" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| // idLen is a length of captcha id string. | ||||
| // (20 bytes of 62-letter alphabet give ~119 bits.) | ||||
| const idLen = 20 | ||||
|  | ||||
| // idChars are characters allowed in captcha id. | ||||
| var idChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") | ||||
|  | ||||
| // rngKey is a secret key used to deterministically derive seeds for | ||||
| // PRNGs used in image and audio. Generated once during initialization. | ||||
| var rngKey [32]byte | ||||
|  | ||||
| func init() { | ||||
| 	if _, err := io.ReadFull(rand.Reader, rngKey[:]); err != nil { | ||||
| 		panic("captcha: error reading random source: " + err.Error()) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Purposes for seed derivation. The goal is to make deterministic PRNG produce | ||||
| // different outputs for images and audio by using different derived seeds. | ||||
| const ( | ||||
| 	imageSeedPurpose = 0x01 | ||||
| 	audioSeedPurpose = 0x02 | ||||
| ) | ||||
|  | ||||
| // deriveSeed returns a 16-byte PRNG seed from rngKey, purpose, id and digits. | ||||
| // Same purpose, id and digits will result in the same derived seed for this | ||||
| // instance of running application. | ||||
| // | ||||
| //   out = HMAC(rngKey, purpose || id || 0x00 || digits)  (cut to 16 bytes) | ||||
| // | ||||
| func deriveSeed(purpose byte, id string, digits []byte) (out [16]byte) { | ||||
| 	var buf [sha256.Size]byte | ||||
| 	h := hmac.New(sha256.New, rngKey[:]) | ||||
| 	h.Write([]byte{purpose}) | ||||
| 	io.WriteString(h, id) | ||||
| 	h.Write([]byte{0}) | ||||
| 	h.Write(digits) | ||||
| 	sum := h.Sum(buf[:0]) | ||||
| 	copy(out[:], sum) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // RandomDigits returns a byte slice of the given length containing | ||||
| // pseudorandom numbers in range 0-9. The slice can be used as a captcha | ||||
| // solution. | ||||
| func RandomDigits(length int) []byte { | ||||
| 	return randomBytesMod(length, 10) | ||||
| } | ||||
|  | ||||
| // randomBytes returns a byte slice of the given length read from CSPRNG. | ||||
| func randomBytes(length int) (b []byte) { | ||||
| 	b = make([]byte, length) | ||||
| 	if _, err := io.ReadFull(rand.Reader, b); err != nil { | ||||
| 		panic("captcha: error reading random source: " + err.Error()) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // randomBytesMod returns a byte slice of the given length, where each byte is | ||||
| // a random number modulo mod. | ||||
| func randomBytesMod(length int, mod byte) (b []byte) { | ||||
| 	if length == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	if mod == 0 { | ||||
| 		panic("captcha: bad mod argument for randomBytesMod") | ||||
| 	} | ||||
| 	maxrb := 255 - byte(256%int(mod)) | ||||
| 	b = make([]byte, length) | ||||
| 	i := 0 | ||||
| 	for { | ||||
| 		r := randomBytes(length + (length / 4)) | ||||
| 		for _, c := range r { | ||||
| 			if c > maxrb { | ||||
| 				// Skip this number to avoid modulo bias. | ||||
| 				continue | ||||
| 			} | ||||
| 			b[i] = c % mod | ||||
| 			i++ | ||||
| 			if i == length { | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| // randomId returns a new random id string. | ||||
| func randomId() string { | ||||
| 	b := randomBytesMod(idLen, byte(len(idChars))) | ||||
| 	for i, c := range b { | ||||
| 		b[i] = idChars[c] | ||||
| 	} | ||||
| 	return string(b) | ||||
| } | ||||
							
								
								
									
										87
									
								
								vendor/github.com/dchest/captcha/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								vendor/github.com/dchest/captcha/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| // Copyright 2011 Dmitry Chestnykh. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package captcha | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"net/http" | ||||
| 	"path" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| type captchaHandler struct { | ||||
| 	imgWidth  int | ||||
| 	imgHeight int | ||||
| } | ||||
|  | ||||
| // Server returns a handler that serves HTTP requests with image or | ||||
| // audio representations of captchas. Image dimensions are accepted as | ||||
| // arguments. The server decides which captcha to serve based on the last URL | ||||
| // path component: file name part must contain a captcha id, file extension — | ||||
| // its format (PNG or WAV). | ||||
| // | ||||
| // For example, for file name "LBm5vMjHDtdUfaWYXiQX.png" it serves an image captcha | ||||
| // with id "LBm5vMjHDtdUfaWYXiQX", and for "LBm5vMjHDtdUfaWYXiQX.wav" it serves the | ||||
| // same captcha in audio format. | ||||
| // | ||||
| // To serve a captcha as a downloadable file, the URL must be constructed in | ||||
| // such a way as if the file to serve is in the "download" subdirectory: | ||||
| // "/download/LBm5vMjHDtdUfaWYXiQX.wav". | ||||
| // | ||||
| // To reload captcha (get a different solution for the same captcha id), append | ||||
| // "?reload=x" to URL, where x may be anything (for example, current time or a | ||||
| // random number to make browsers refetch an image instead of loading it from | ||||
| // cache). | ||||
| // | ||||
| // By default, the Server serves audio in English language. To serve audio | ||||
| // captcha in one of the other supported languages, append "lang" value, for | ||||
| // example, "?lang=ru". | ||||
| func Server(imgWidth, imgHeight int) http.Handler { | ||||
| 	return &captchaHandler{imgWidth, imgHeight} | ||||
| } | ||||
|  | ||||
| func (h *captchaHandler) serve(w http.ResponseWriter, r *http.Request, id, ext, lang string, download bool) error { | ||||
| 	w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") | ||||
| 	w.Header().Set("Pragma", "no-cache") | ||||
| 	w.Header().Set("Expires", "0") | ||||
|  | ||||
| 	var content bytes.Buffer | ||||
| 	switch ext { | ||||
| 	case ".png": | ||||
| 		w.Header().Set("Content-Type", "image/png") | ||||
| 		WriteImage(&content, id, h.imgWidth, h.imgHeight) | ||||
| 	case ".wav": | ||||
| 		w.Header().Set("Content-Type", "audio/x-wav") | ||||
| 		WriteAudio(&content, id, lang) | ||||
| 	default: | ||||
| 		return ErrNotFound | ||||
| 	} | ||||
|  | ||||
| 	if download { | ||||
| 		w.Header().Set("Content-Type", "application/octet-stream") | ||||
| 	} | ||||
| 	http.ServeContent(w, r, id+ext, time.Time{}, bytes.NewReader(content.Bytes())) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (h *captchaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||||
| 	dir, file := path.Split(r.URL.Path) | ||||
| 	ext := path.Ext(file) | ||||
| 	id := file[:len(file)-len(ext)] | ||||
| 	if ext == "" || id == "" { | ||||
| 		http.NotFound(w, r) | ||||
| 		return | ||||
| 	} | ||||
| 	if r.FormValue("reload") != "" { | ||||
| 		Reload(id) | ||||
| 	} | ||||
| 	lang := strings.ToLower(r.FormValue("lang")) | ||||
| 	download := path.Base(dir) == "download" | ||||
| 	if h.serve(w, r, id, ext, lang, download) == ErrNotFound { | ||||
| 		http.NotFound(w, r) | ||||
| 	} | ||||
| 	// Ignore other errors. | ||||
| } | ||||
							
								
								
									
										278
									
								
								vendor/github.com/dchest/captcha/siprng.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								vendor/github.com/dchest/captcha/siprng.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,278 @@ | ||||
| // Copyright 2014 Dmitry Chestnykh. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package captcha | ||||
|  | ||||
| import "encoding/binary" | ||||
|  | ||||
| // siprng is PRNG based on SipHash-2-4. | ||||
| // (Note: it's not safe to use a single siprng from multiple goroutines.) | ||||
| type siprng struct { | ||||
| 	k0, k1, ctr uint64 | ||||
| } | ||||
|  | ||||
| // siphash implements SipHash-2-4, accepting a uint64 as a message. | ||||
| func siphash(k0, k1, m uint64) uint64 { | ||||
| 	// Initialization. | ||||
| 	v0 := k0 ^ 0x736f6d6570736575 | ||||
| 	v1 := k1 ^ 0x646f72616e646f6d | ||||
| 	v2 := k0 ^ 0x6c7967656e657261 | ||||
| 	v3 := k1 ^ 0x7465646279746573 | ||||
| 	t := uint64(8) << 56 | ||||
|  | ||||
| 	// Compression. | ||||
| 	v3 ^= m | ||||
|  | ||||
| 	// Round 1. | ||||
| 	v0 += v1 | ||||
| 	v1 = v1<<13 | v1>>(64-13) | ||||
| 	v1 ^= v0 | ||||
| 	v0 = v0<<32 | v0>>(64-32) | ||||
|  | ||||
| 	v2 += v3 | ||||
| 	v3 = v3<<16 | v3>>(64-16) | ||||
| 	v3 ^= v2 | ||||
|  | ||||
| 	v0 += v3 | ||||
| 	v3 = v3<<21 | v3>>(64-21) | ||||
| 	v3 ^= v0 | ||||
|  | ||||
| 	v2 += v1 | ||||
| 	v1 = v1<<17 | v1>>(64-17) | ||||
| 	v1 ^= v2 | ||||
| 	v2 = v2<<32 | v2>>(64-32) | ||||
|  | ||||
| 	// Round 2. | ||||
| 	v0 += v1 | ||||
| 	v1 = v1<<13 | v1>>(64-13) | ||||
| 	v1 ^= v0 | ||||
| 	v0 = v0<<32 | v0>>(64-32) | ||||
|  | ||||
| 	v2 += v3 | ||||
| 	v3 = v3<<16 | v3>>(64-16) | ||||
| 	v3 ^= v2 | ||||
|  | ||||
| 	v0 += v3 | ||||
| 	v3 = v3<<21 | v3>>(64-21) | ||||
| 	v3 ^= v0 | ||||
|  | ||||
| 	v2 += v1 | ||||
| 	v1 = v1<<17 | v1>>(64-17) | ||||
| 	v1 ^= v2 | ||||
| 	v2 = v2<<32 | v2>>(64-32) | ||||
|  | ||||
| 	v0 ^= m | ||||
|  | ||||
| 	// Compress last block. | ||||
| 	v3 ^= t | ||||
|  | ||||
| 	// Round 1. | ||||
| 	v0 += v1 | ||||
| 	v1 = v1<<13 | v1>>(64-13) | ||||
| 	v1 ^= v0 | ||||
| 	v0 = v0<<32 | v0>>(64-32) | ||||
|  | ||||
| 	v2 += v3 | ||||
| 	v3 = v3<<16 | v3>>(64-16) | ||||
| 	v3 ^= v2 | ||||
|  | ||||
| 	v0 += v3 | ||||
| 	v3 = v3<<21 | v3>>(64-21) | ||||
| 	v3 ^= v0 | ||||
|  | ||||
| 	v2 += v1 | ||||
| 	v1 = v1<<17 | v1>>(64-17) | ||||
| 	v1 ^= v2 | ||||
| 	v2 = v2<<32 | v2>>(64-32) | ||||
|  | ||||
| 	// Round 2. | ||||
| 	v0 += v1 | ||||
| 	v1 = v1<<13 | v1>>(64-13) | ||||
| 	v1 ^= v0 | ||||
| 	v0 = v0<<32 | v0>>(64-32) | ||||
|  | ||||
| 	v2 += v3 | ||||
| 	v3 = v3<<16 | v3>>(64-16) | ||||
| 	v3 ^= v2 | ||||
|  | ||||
| 	v0 += v3 | ||||
| 	v3 = v3<<21 | v3>>(64-21) | ||||
| 	v3 ^= v0 | ||||
|  | ||||
| 	v2 += v1 | ||||
| 	v1 = v1<<17 | v1>>(64-17) | ||||
| 	v1 ^= v2 | ||||
| 	v2 = v2<<32 | v2>>(64-32) | ||||
|  | ||||
| 	v0 ^= t | ||||
|  | ||||
| 	// Finalization. | ||||
| 	v2 ^= 0xff | ||||
|  | ||||
| 	// Round 1. | ||||
| 	v0 += v1 | ||||
| 	v1 = v1<<13 | v1>>(64-13) | ||||
| 	v1 ^= v0 | ||||
| 	v0 = v0<<32 | v0>>(64-32) | ||||
|  | ||||
| 	v2 += v3 | ||||
| 	v3 = v3<<16 | v3>>(64-16) | ||||
| 	v3 ^= v2 | ||||
|  | ||||
| 	v0 += v3 | ||||
| 	v3 = v3<<21 | v3>>(64-21) | ||||
| 	v3 ^= v0 | ||||
|  | ||||
| 	v2 += v1 | ||||
| 	v1 = v1<<17 | v1>>(64-17) | ||||
| 	v1 ^= v2 | ||||
| 	v2 = v2<<32 | v2>>(64-32) | ||||
|  | ||||
| 	// Round 2. | ||||
| 	v0 += v1 | ||||
| 	v1 = v1<<13 | v1>>(64-13) | ||||
| 	v1 ^= v0 | ||||
| 	v0 = v0<<32 | v0>>(64-32) | ||||
|  | ||||
| 	v2 += v3 | ||||
| 	v3 = v3<<16 | v3>>(64-16) | ||||
| 	v3 ^= v2 | ||||
|  | ||||
| 	v0 += v3 | ||||
| 	v3 = v3<<21 | v3>>(64-21) | ||||
| 	v3 ^= v0 | ||||
|  | ||||
| 	v2 += v1 | ||||
| 	v1 = v1<<17 | v1>>(64-17) | ||||
| 	v1 ^= v2 | ||||
| 	v2 = v2<<32 | v2>>(64-32) | ||||
|  | ||||
| 	// Round 3. | ||||
| 	v0 += v1 | ||||
| 	v1 = v1<<13 | v1>>(64-13) | ||||
| 	v1 ^= v0 | ||||
| 	v0 = v0<<32 | v0>>(64-32) | ||||
|  | ||||
| 	v2 += v3 | ||||
| 	v3 = v3<<16 | v3>>(64-16) | ||||
| 	v3 ^= v2 | ||||
|  | ||||
| 	v0 += v3 | ||||
| 	v3 = v3<<21 | v3>>(64-21) | ||||
| 	v3 ^= v0 | ||||
|  | ||||
| 	v2 += v1 | ||||
| 	v1 = v1<<17 | v1>>(64-17) | ||||
| 	v1 ^= v2 | ||||
| 	v2 = v2<<32 | v2>>(64-32) | ||||
|  | ||||
| 	// Round 4. | ||||
| 	v0 += v1 | ||||
| 	v1 = v1<<13 | v1>>(64-13) | ||||
| 	v1 ^= v0 | ||||
| 	v0 = v0<<32 | v0>>(64-32) | ||||
|  | ||||
| 	v2 += v3 | ||||
| 	v3 = v3<<16 | v3>>(64-16) | ||||
| 	v3 ^= v2 | ||||
|  | ||||
| 	v0 += v3 | ||||
| 	v3 = v3<<21 | v3>>(64-21) | ||||
| 	v3 ^= v0 | ||||
|  | ||||
| 	v2 += v1 | ||||
| 	v1 = v1<<17 | v1>>(64-17) | ||||
| 	v1 ^= v2 | ||||
| 	v2 = v2<<32 | v2>>(64-32) | ||||
|  | ||||
| 	return v0 ^ v1 ^ v2 ^ v3 | ||||
| } | ||||
|  | ||||
| // Seed sets a new secret seed for PRNG. | ||||
| func (p *siprng) Seed(k [16]byte) { | ||||
| 	p.k0 = binary.LittleEndian.Uint64(k[0:8]) | ||||
| 	p.k1 = binary.LittleEndian.Uint64(k[8:16]) | ||||
| 	p.ctr = 1 | ||||
| } | ||||
|  | ||||
| // Uint64 returns a new pseudorandom uint64. | ||||
| func (p *siprng) Uint64() uint64 { | ||||
| 	v := siphash(p.k0, p.k1, p.ctr) | ||||
| 	p.ctr++ | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| func (p *siprng) Bytes(n int) []byte { | ||||
| 	// Since we don't have a buffer for generated bytes in siprng state, | ||||
| 	// we just generate enough 8-byte blocks and then cut the result to the | ||||
| 	// required length. Doing it this way, we lose generated bytes, and we | ||||
| 	// don't get the strictly sequential deterministic output from PRNG: | ||||
| 	// calling Uint64() and then Bytes(3) produces different output than | ||||
| 	// when calling them in the reverse order, but for our applications | ||||
| 	// this is OK. | ||||
| 	numBlocks := (n + 8 - 1) / 8 | ||||
| 	b := make([]byte, numBlocks*8) | ||||
| 	for i := 0; i < len(b); i += 8 { | ||||
| 		binary.LittleEndian.PutUint64(b[i:], p.Uint64()) | ||||
| 	} | ||||
| 	return b[:n] | ||||
| } | ||||
|  | ||||
| func (p *siprng) Int63() int64 { | ||||
| 	return int64(p.Uint64() & 0x7fffffffffffffff) | ||||
| } | ||||
|  | ||||
| func (p *siprng) Uint32() uint32 { | ||||
| 	return uint32(p.Uint64()) | ||||
| } | ||||
|  | ||||
| func (p *siprng) Int31() int32 { | ||||
| 	return int32(p.Uint32() & 0x7fffffff) | ||||
| } | ||||
|  | ||||
| func (p *siprng) Intn(n int) int { | ||||
| 	if n <= 0 { | ||||
| 		panic("invalid argument to Intn") | ||||
| 	} | ||||
| 	if n <= 1<<31-1 { | ||||
| 		return int(p.Int31n(int32(n))) | ||||
| 	} | ||||
| 	return int(p.Int63n(int64(n))) | ||||
| } | ||||
|  | ||||
| func (p *siprng) Int63n(n int64) int64 { | ||||
| 	if n <= 0 { | ||||
| 		panic("invalid argument to Int63n") | ||||
| 	} | ||||
| 	max := int64((1 << 63) - 1 - (1<<63)%uint64(n)) | ||||
| 	v := p.Int63() | ||||
| 	for v > max { | ||||
| 		v = p.Int63() | ||||
| 	} | ||||
| 	return v % n | ||||
| } | ||||
|  | ||||
| func (p *siprng) Int31n(n int32) int32 { | ||||
| 	if n <= 0 { | ||||
| 		panic("invalid argument to Int31n") | ||||
| 	} | ||||
| 	max := int32((1 << 31) - 1 - (1<<31)%uint32(n)) | ||||
| 	v := p.Int31() | ||||
| 	for v > max { | ||||
| 		v = p.Int31() | ||||
| 	} | ||||
| 	return v % n | ||||
| } | ||||
|  | ||||
| func (p *siprng) Float64() float64 { return float64(p.Int63()) / (1 << 63) } | ||||
|  | ||||
| // Int returns a pseudorandom int in range [from, to]. | ||||
| func (p *siprng) Int(from, to int) int { | ||||
| 	return p.Intn(to+1-from) + from | ||||
| } | ||||
|  | ||||
| // Float returns a pseudorandom float64 in range [from, to]. | ||||
| func (p *siprng) Float(from, to float64) float64 { | ||||
| 	return (to-from)*p.Float64() + from | ||||
| } | ||||
							
								
								
									
										9726
									
								
								vendor/github.com/dchest/captcha/sounds.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9726
									
								
								vendor/github.com/dchest/captcha/sounds.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										117
									
								
								vendor/github.com/dchest/captcha/store.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								vendor/github.com/dchest/captcha/store.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,117 @@ | ||||
| // Copyright 2011 Dmitry Chestnykh. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package captcha | ||||
|  | ||||
| import ( | ||||
| 	"container/list" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // An object implementing Store interface can be registered with SetCustomStore | ||||
| // function to handle storage and retrieval of captcha ids and solutions for | ||||
| // them, replacing the default memory store. | ||||
| // | ||||
| // It is the responsibility of an object to delete expired and used captchas | ||||
| // when necessary (for example, the default memory store collects them in Set | ||||
| // method after the certain amount of captchas has been stored.) | ||||
| type Store interface { | ||||
| 	// Set sets the digits for the captcha id. | ||||
| 	Set(id string, digits []byte) | ||||
|  | ||||
| 	// Get returns stored digits for the captcha id. Clear indicates | ||||
| 	// whether the captcha must be deleted from the store. | ||||
| 	Get(id string, clear bool) (digits []byte) | ||||
| } | ||||
|  | ||||
| // expValue stores timestamp and id of captchas. It is used in the list inside | ||||
| // memoryStore for indexing generated captchas by timestamp to enable garbage | ||||
| // collection of expired captchas. | ||||
| type idByTimeValue struct { | ||||
| 	timestamp time.Time | ||||
| 	id        string | ||||
| } | ||||
|  | ||||
| // memoryStore is an internal store for captcha ids and their values. | ||||
| type memoryStore struct { | ||||
| 	sync.RWMutex | ||||
| 	digitsById map[string][]byte | ||||
| 	idByTime   *list.List | ||||
| 	// Number of items stored since last collection. | ||||
| 	numStored int | ||||
| 	// Number of saved items that triggers collection. | ||||
| 	collectNum int | ||||
| 	// Expiration time of captchas. | ||||
| 	expiration time.Duration | ||||
| } | ||||
|  | ||||
| // NewMemoryStore returns a new standard memory store for captchas with the | ||||
| // given collection threshold and expiration time (duration). The returned | ||||
| // store must be registered with SetCustomStore to replace the default one. | ||||
| func NewMemoryStore(collectNum int, expiration time.Duration) Store { | ||||
| 	s := new(memoryStore) | ||||
| 	s.digitsById = make(map[string][]byte) | ||||
| 	s.idByTime = list.New() | ||||
| 	s.collectNum = collectNum | ||||
| 	s.expiration = expiration | ||||
| 	return s | ||||
| } | ||||
|  | ||||
| func (s *memoryStore) Set(id string, digits []byte) { | ||||
| 	s.Lock() | ||||
| 	s.digitsById[id] = digits | ||||
| 	s.idByTime.PushBack(idByTimeValue{time.Now(), id}) | ||||
| 	s.numStored++ | ||||
| 	if s.numStored <= s.collectNum { | ||||
| 		s.Unlock() | ||||
| 		return | ||||
| 	} | ||||
| 	s.Unlock() | ||||
| 	go s.collect() | ||||
| } | ||||
|  | ||||
| func (s *memoryStore) Get(id string, clear bool) (digits []byte) { | ||||
| 	if !clear { | ||||
| 		// When we don't need to clear captcha, acquire read lock. | ||||
| 		s.RLock() | ||||
| 		defer s.RUnlock() | ||||
| 	} else { | ||||
| 		s.Lock() | ||||
| 		defer s.Unlock() | ||||
| 	} | ||||
| 	digits, ok := s.digitsById[id] | ||||
| 	if !ok { | ||||
| 		return | ||||
| 	} | ||||
| 	if clear { | ||||
| 		delete(s.digitsById, id) | ||||
| 		// XXX(dchest) Index (s.idByTime) will be cleaned when | ||||
| 		// collecting expired captchas.  Can't clean it here, because | ||||
| 		// we don't store reference to expValue in the map. | ||||
| 		// Maybe store it? | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (s *memoryStore) collect() { | ||||
| 	now := time.Now() | ||||
| 	s.Lock() | ||||
| 	defer s.Unlock() | ||||
| 	s.numStored = 0 | ||||
| 	for e := s.idByTime.Front(); e != nil; { | ||||
| 		ev, ok := e.Value.(idByTimeValue) | ||||
| 		if !ok { | ||||
| 			return | ||||
| 		} | ||||
| 		if ev.timestamp.Add(s.expiration).Before(now) { | ||||
| 			delete(s.digitsById, ev.id) | ||||
| 			next := e.Next() | ||||
| 			s.idByTime.Remove(e) | ||||
| 			e = next | ||||
| 		} else { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user