sharepoint2013fba /Visigo.Sharepoint.FormsBasedAuthentication/Visigo.Sharepoint.FormsBasedAuthentication/Layouts/FBA/ImageHipChallenge.ashx

Language Unknown Lines 223
MD5 Hash 9a012f48394fc7ea39db1fd394127ba4
Repository https://hg.codeplex.com/sharepoint2013fba View Raw File
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
<%@ WebHandler Language="C#" Class="Visigo.Sharepoint.FormsBasedAuthentication.HIP.ImageHipChallengeHandler" %>
using System;
using System.Web;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Collections.Specialized;
using System.Security.Cryptography;

namespace Visigo.Sharepoint.FormsBasedAuthentication.HIP
{
    /// <summary>Provides cryptographically-strong pseudo-random numbers.</summary>
    internal class RandomNumbers
    {
        /// <summary>Random number generator.</summary>
        private RNGCryptoServiceProvider _rand = new RNGCryptoServiceProvider();

        /// <summary>Used by NextRandom and NextRandomDouble.</summary>
        /// <remarks>
        /// RNGCryptoServiceProvider is not thread-safe.  Therefore, we only call to on a single thread.
        /// As a result, we can resuse the same byte arrays for every call.
        /// </remarks>
        private byte[] _rd4 = new byte[4], _rd8 = new byte[8];

        /// <summary>Gets a random non-negative integer less than max.</summary>
        /// <param name="max">The upper-bound for the random number.</param>
        /// <returns>The generated random number.</returns>
        public int Next(int max)
        {
            if (max <= 0) throw new ArgumentOutOfRangeException("max");
            _rand.GetBytes(_rd4);
            int val = BitConverter.ToInt32(_rd4, 0) % max;
            if (val < 0) val = -val;
            return val;
        }

        /// <summary>Gets a random number between min and max, inclusive.</summary>
        /// <param name="min">The minimum possible value.</param>
        /// <param name="max">The maximum possible value.</param>
        /// <returns>The randomly generated number.</returns>
        public int Next(int min, int max)
        {
            if (min > max) throw new ArgumentOutOfRangeException("max");
            return Next(max - min + 1) + min;
        }

        /// <summary>Gets a randomly generated double between 0.0 and 1.1.</summary>
        /// <returns>The random number.</returns>
        public double NextDouble()
        {
            _rand.GetBytes(_rd8);
            return BitConverter.ToUInt64(_rd8, 0) / (double)UInt64.MaxValue;
        }
    }
    
	/// <summary>Handles requests for dynamic images from the ImageHipChallenge control.</summary>
	public class ImageHipChallengeHandler : IHttpHandler
	{
        /// <summary>Default value for the RenderUrl property.</summary>
        private const string RENDERURL_DEFAULT = "ImageHipChallenge.ashx";
        /// <summary>Query string key for the image width.</summary>
        internal const string WIDTH_KEY = "w";
        /// <summary>Query string key for the image height.</summary>
        internal const string HEIGHT_KEY = "h";
        /// <summary>Query string key for challenge ID.</summary>
        internal const string ID_KEY = "id";        
        private RandomNumbers _rand = new RandomNumbers();
        
		/// <summary>Maximum width of an image to generate.</summary>
		private const int MAX_IMAGE_WIDTH = 600;
		/// <summary>Maximum height of an image to generate.</summary>
		private const int MAX_IMAGE_HEIGHT = 600;

		/// <summary>Gets whether this handler is reusable.</summary>
		/// <remarks>This handler is not thread-safe (uses non thread-safe member variables), so it is not reusable.</remarks>
		public bool IsReusable { get { return false; } }

        /// <summary>Gets a random non-negative integer less than max.</summary>
        /// <param name="max">The upper-bound for the random number.</param>
        /// <returns>The generated random number.</returns>
        protected int NextRandom(int max) { return _rand.Next(max); }

        /// <summary>Gets a random number between min and max, inclusive.</summary>
        /// <param name="min">The minimum possible value.</param>
        /// <param name="max">The maximum possible value.</param>
        /// <returns>The randomly generated number.</returns>
        protected int NextRandom(int min, int max) { return _rand.Next(min, max); }

        /// <summary>Gets a randomly generated double between 0.0 and 1.1.</summary>
        /// <returns>The random number.</returns>
        protected double NextRandomDouble() { return _rand.NextDouble(); }

        /// <summary>Gets the challenge text for a particular ID.</summary>
        /// <param name="challengeId">The ID of the challenge text to retrieve.</param>
        /// <returns>The text associated with the specified ID; null if no text exists.</returns>
        internal static string GetChallengeText(Guid challengeId)
        {
            HttpContext ctx = HttpContext.Current;
            return (string)ctx.Cache[challengeId.ToString()];
        }
                
		/// <summary>Processes the image request and generates the appropriate image.</summary>
		/// <param name="context">The current HttpContext.</param>
		public void ProcessRequest(HttpContext context)
		{
			// Retrieve query parameters and challenge text
            NameValueCollection queryString = context.Request.QueryString;
            int width = Convert.ToInt32(queryString[WIDTH_KEY]);
            if (width <= 0 || width > MAX_IMAGE_WIDTH) throw new ArgumentOutOfRangeException(WIDTH_KEY);
            int height = Convert.ToInt32(queryString[HEIGHT_KEY]);
            if (height <= 0 || height > MAX_IMAGE_HEIGHT) throw new ArgumentOutOfRangeException(HEIGHT_KEY);
            string text = GetChallengeText(new Guid(queryString[ID_KEY]));

            if (text != null)
            {
                // We successfully retrieved the information, so generate the image and send it to the client.
                HttpResponse resp = context.Response;
                resp.Clear();
                resp.ContentType = "image/jpeg";
                using (Bitmap bmp = GenerateImage(text, new Size(width, height)))
                {
                    bmp.Save(resp.OutputStream, ImageFormat.Jpeg);
                }
            }
		}

		/// <summary>Generates the challenge image.</summary>
		/// <param name="text">The text to be rendered into the image.</param>
		/// <param name="size">The size of the image to generate.</param>
		/// <returns>A dynamically-generated challenge image.</returns>
		public Bitmap GenerateImage(string text, Size size)
		{
			// Create the new Bitmap of the specified size and render to it
			Bitmap bmp = new Bitmap(size.Width, size.Height);
			using (Graphics g = Graphics.FromImage(bmp))
			{
				// Draw the background as a random linear gradient
				using(Brush b = new LinearGradientBrush(
						  new Rectangle(0,0,size.Width,size.Height),
						  Color.FromArgb(NextRandom(256),NextRandom(256),NextRandom(256)),
						  Color.FromArgb(NextRandom(256),NextRandom(256),NextRandom(256)),
						  (float)(NextRandomDouble()*360),false))
				{
					g.FillRectangle(b, 0, 0, bmp.Width, bmp.Height);
				}

				// Select a font family and create the default sized font.  We then need to shrink
				// the font size until the text fits.
				FontFamily ff = _families[NextRandom(_families.Length)];
				int emSize = (int)(size.Width*2 / text.Length);
				Font f = new Font(ff, emSize);
				try
				{
					// Make sure that the font size we have will fit with the selected text.
					SizeF measured = new SizeF(0,0);
					SizeF workingSize = new SizeF(size.Width, size.Height);
					while (emSize > 2 &&
						(measured = g.MeasureString(text, f)).Width > workingSize.Width ||
						measured.Height > workingSize.Height)
					{
						f.Dispose();
						f = new Font(ff, emSize -= 2);
					}

					// Select a color and draw the string into the center of the image
					using(StringFormat fmt = new StringFormat())
					{
						fmt.Alignment = fmt.LineAlignment = StringAlignment.Center;
						using(Brush b = new LinearGradientBrush(
								  new Rectangle(0,0,size.Width/2,size.Height/2),
								  Color.FromArgb(NextRandom(256),NextRandom(256),NextRandom(256)),
								  Color.FromArgb(NextRandom(256),NextRandom(256),NextRandom(256)),
								  (float)(NextRandomDouble()*360),false))
						{
							g.DrawString(text, f, b, new Rectangle(0,0,bmp.Width,bmp.Height), fmt);
						}
					}
				}
				finally
				{
					// Clean up
					f.Dispose();
				}
			}
			
			// Distort the final image and return it.  This distortion amount is fairly arbitrary.
			DistortImage(bmp, NextRandom(5, 10) * (NextRandom(2) == 1 ? 1 : -1) );
			return bmp;
		}

		/// <summary>Distorts the image.</summary>
		/// <param name="b">The image to be transformed.</param>
		/// <param name="distortion">An amount of distortion.</param>
		private static void DistortImage(Bitmap b, double distortion)
		{
			int width = b.Width, height = b.Height;

			// Copy the image so that we're always using the original for source color
			using (Bitmap copy = (Bitmap)b.Clone())
			{
				// Iterate over every pixel
				for (int y = 0; y < height; y++)
				{
					for (int x = 0; x < width; x++)
					{
						// Adds a simple wave
						int newX = (int)(x + (distortion * Math.Sin(Math.PI * y / 64.0)));
						int newY = (int)(y + (distortion * Math.Cos(Math.PI * x / 64.0)));
						if (newX < 0 || newX >= width) newX = 0;
						if (newY < 0 || newY >= height) newY = 0;
						b.SetPixel(x, y, copy.GetPixel(newX, newY));
					}
				}
			}
		}

		/// <summary>List of fonts that can be used for rendering text.</summary>
		/// <remarks>This list can be changed to include any families available on the current system.</remarks>
		private static FontFamily [] _families = { 
													 new FontFamily("Times New Roman")
												 };
	}
}
Back to Top