2 // System.Web.UI.ObjectStateFormatter
5 // Ben Maurer (bmaurer@users.sourceforge.net)
6 // Gonzalo Paniagua (gonzalo@ximian.com)
9 // (c) Copyright 2004-2010 Novell, Inc. (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Collections;
36 using System.ComponentModel;
37 using System.Globalization;
40 using System.Reflection;
41 using System.Runtime.Serialization.Formatters.Binary;
42 using System.Runtime.Serialization;
44 using System.Web.UI.WebControls;
45 using System.Web.Util;
46 using System.Diagnostics;
47 using System.Security.Cryptography;
48 using System.Web.Configuration;
50 namespace System.Web.UI
52 public sealed class ObjectStateFormatter : IFormatter, IStateFormatter
58 public ObjectStateFormatter ()
62 internal ObjectStateFormatter (Page page)
67 internal ObjectStateFormatter (byte [] vkey)
72 internal bool EnableMac {
79 return page.EnableViewStateMac;
83 internal HashAlgorithm GetAlgo ()
92 MachineKeySection mconfig = (MachineKeySection) WebConfigurationManager.GetWebApplicationSection ("system.web/machineKey");
93 algoKey = MachineKeySectionUtils.ValidationKeyBytes (mconfig);
97 algo = new HMACSHA1 (algoKey);
101 static int ValidateInput (HashAlgorithm algo, byte [] data, int offset, int size)
104 throw new HttpException ("Unable to validate data.");
106 int hash_size = algo.HashSize / 8;
107 if (size != 0 && size < hash_size)
108 throw new HttpException ("Unable to validate data.");
110 int data_length = size - hash_size;
111 MemoryStream data_stream = new MemoryStream (data, offset, data_length, false, false);
112 byte [] hash = algo.ComputeHash (data_stream);
113 for (int i = 0; i < hash_size; i++) {
114 if (hash [i] != data [data_length + i])
115 throw new HttpException ("Unable to validate data.");
120 public object Deserialize (Stream inputStream)
122 if (inputStream == null)
123 throw new ArgumentNullException ("inputStream");
125 return DeserializeObject (new BinaryReader (inputStream));
128 public object Deserialize (string inputString)
130 if (inputString == null)
131 throw new ArgumentNullException ("inputString");
132 if (inputString.Length == 0)
133 throw new ArgumentNullException ("inputString");
135 byte [] buffer = Convert.FromBase64String (inputString);
137 if (buffer == null || (length = buffer.Length) == 0)
138 throw new ArgumentNullException ("inputString");
139 if (page != null && EnableMac)
140 length = ValidateInput (GetAlgo (), buffer, 0, length);
142 bool isEncrypted = ((int)buffer [--length] == 1)? true : false;
143 Stream ms = new MemoryStream (buffer, 0, length, false, false);
145 ms = new CryptoStream (ms, page.GetCryptoTransform (CryptoStreamMode.Read), CryptoStreamMode.Read);
146 return Deserialize (ms);
149 public string Serialize (object stateGraph)
151 if (stateGraph == null)
154 MemoryStream ms = new MemoryStream ();
156 bool needEncryption = page == null ? false : page.NeedViewStateEncryption;
158 output = new CryptoStream (output, page.GetCryptoTransform (CryptoStreamMode.Write), CryptoStreamMode.Write);
160 Serialize (output, stateGraph);
161 ms.WriteByte((byte)(needEncryption? 1 : 0));
163 ms.WriteTo (File.OpenWrite (Path.GetTempFileName ()));
165 if (EnableMac && ms.Length > 0) {
166 HashAlgorithm algo = GetAlgo ();
168 byte [] hash = algo.ComputeHash (ms.GetBuffer (), 0, (int) ms.Length);
169 ms.Write (hash, 0, hash.Length);
173 return Convert.ToBase64String (ms.GetBuffer (), 0, (int) ms.Length);
176 public void Serialize (Stream outputStream, object stateGraph)
178 if (outputStream == null)
179 throw new ArgumentNullException ("outputStream");
181 if (stateGraph == null)
182 throw new ArgumentNullException ("stateGraph");
184 SerializeValue (new BinaryWriter (outputStream), stateGraph);
187 void SerializeValue (BinaryWriter w, object o)
189 ObjectFormatter.WriteObject (w, o, new WriterContext ());
192 object DeserializeObject (BinaryReader r)
194 return ObjectFormatter.ReadObject (r, new ReaderContext ());
199 object IFormatter.Deserialize (Stream serializationStream)
201 return Deserialize (serializationStream);
204 void IFormatter.Serialize (Stream serializationStream, object stateGraph)
206 Serialize (serializationStream, stateGraph);
209 SerializationBinder IFormatter.Binder {
214 StreamingContext IFormatter.Context {
215 get { return new StreamingContext (StreamingContextStates.All); }
219 ISurrogateSelector IFormatter.SurrogateSelector {
226 #region Object Readers/Writers
228 sealed class WriterContext
238 public bool RegisterCache (object o)
240 if (nextKey == short.MaxValue)
244 cache = new Hashtable ();
245 cache.Add (o, key = nextKey++);
249 object posKey = cache [o];
250 if (posKey == null) {
251 cache.Add (o, key = nextKey++);
255 key = (short) posKey;
260 sealed class ReaderContext
264 public void CacheItem (object o)
267 cache = new ArrayList ();
272 public object GetCache (short key)
278 abstract class ObjectFormatter
280 static readonly Hashtable writeMap = new Hashtable ();
281 static ObjectFormatter [] readMap = new ObjectFormatter [256];
282 static BinaryObjectFormatter binaryObjectFormatter;
283 static TypeFormatter typeFormatter;
284 static EnumFormatter enumFormatter;
285 static SingleRankArrayFormatter singleRankArrayFormatter;
286 static TypeConverterFormatter typeConverterFormatter;
288 static ObjectFormatter ()
290 new StringFormatter ().Register ();
291 new Int64Formatter ().Register ();
292 new Int32Formatter ().Register ();
293 new Int16Formatter ().Register ();
294 new ByteFormatter ().Register ();
295 new BooleanFormatter ().Register ();
296 new CharFormatter ().Register ();
297 new DateTimeFormatter ().Register ();
298 new PairFormatter ().Register ();
299 new TripletFormatter ().Register ();
300 new ArrayListFormatter ().Register ();
301 new HashtableFormatter ().Register ();
302 new ObjectArrayFormatter ().Register ();
303 new UnitFormatter ().Register ();
304 new FontUnitFormatter ().Register ();
305 new IndexedStringFormatter ().Register ();
306 new ColorFormatter ().Register ();
308 enumFormatter = new EnumFormatter ();
309 enumFormatter.Register ();
311 typeFormatter = new TypeFormatter ();
312 typeFormatter.Register ();
314 singleRankArrayFormatter = new SingleRankArrayFormatter ();
315 singleRankArrayFormatter.Register ();
317 typeConverterFormatter = new TypeConverterFormatter ();
318 typeConverterFormatter.Register ();
320 binaryObjectFormatter = new BinaryObjectFormatter ();
321 binaryObjectFormatter.Register ();
325 static byte nextId = 1;
327 public ObjectFormatter ()
329 PrimaryId = nextId ++;
330 if (NumberOfIds == 1)
333 SecondaryId = nextId ++;
334 if (NumberOfIds == 2)
337 TertiaryId = nextId ++;
338 if (NumberOfIds == 3)
341 throw new Exception ();
344 protected readonly byte PrimaryId, SecondaryId = 255, TertiaryId = 255;
346 protected abstract void Write (BinaryWriter w, object o, WriterContext ctx);
347 protected abstract object Read (byte token, BinaryReader r, ReaderContext ctx);
348 protected abstract Type Type { get; }
349 protected virtual int NumberOfIds { get { return 1; } }
351 public virtual void Register ()
353 writeMap [Type] = this;
354 readMap [PrimaryId] = this;
355 if (SecondaryId != 255) {
356 readMap [SecondaryId] = this;
357 if (TertiaryId != 255)
358 readMap [TertiaryId] = this;
362 public static void WriteObject (BinaryWriter w, object o, WriterContext ctx)
364 #if TRACE && !TARGET_J2EE
366 Trace.WriteLine (String.Format ("Writing {0} (type: {1})", o, o.GetType ()));
369 Trace.WriteLine ("Writing null");
371 long pos = w.BaseStream.Position;
379 Type t = o.GetType ();
381 Trace.WriteLine (String.Format ("Looking up formatter for type {0}", t));
384 ObjectFormatter fmt = writeMap [t] as ObjectFormatter;
386 Trace.WriteLine (String.Format ("Formatter from writeMap: '{0}'", fmt));
389 // Handle abstract types here
395 else if (t.IsArray && ((Array) o).Rank == 1)
396 fmt = singleRankArrayFormatter;
398 TypeConverter converter;
399 converter = TypeDescriptor.GetConverter (o);
401 Trace.WriteLine (String.Format ("Type converter: '{0}' (to string: {1}; from {2}: {3})",
403 converter != null ? converter.CanConvertTo (typeof (string)) : false,
405 converter != null ? converter.CanConvertFrom (t) : false));
407 // Do not use the converter if it's an instance of
408 // TypeConverter itself - it reports it is able to
409 // convert to string, but it's only a conversion
410 // consisting of a call to ToString() with no
411 // reverse conversion supported. This leads to
412 // problems when deserializing the object.
413 if (converter == null || converter.GetType () == typeof (TypeConverter) ||
414 !converter.CanConvertTo (typeof (string)) || !converter.CanConvertFrom (typeof (string)))
415 fmt = binaryObjectFormatter;
417 typeConverterFormatter.Converter = converter;
418 fmt = typeConverterFormatter;
424 Trace.WriteLine (String.Format ("Writing with formatter '{0}'", fmt.GetType ()));
426 fmt.Write (w, o, ctx);
427 #if TRACE && !TARGET_J2EE
429 Trace.WriteLine (String.Format ("Wrote {0} (type: {1}) {2} bytes", o, o.GetType (), w.BaseStream.Position - pos));
433 public static object ReadObject (BinaryReader r, ReaderContext ctx)
435 byte sig = r.ReadByte ();
440 return readMap [sig].Read (sig, r, ctx);
443 protected void Write7BitEncodedInt (BinaryWriter w, int value)
446 int high = (value >> 7) & 0x01ffffff;
447 byte b = (byte)(value & 0x7f);
450 b = (byte)(b | 0x80);
457 protected int Read7BitEncodedInt (BinaryReader r)
466 ret = ret | ((b & 0x7f) << shift);
468 } while ((b & 0x80) == 0x80);
474 #region Primitive Formatters
475 class StringFormatter : ObjectFormatter
477 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
479 if (ctx.RegisterCache (o)) {
480 w.Write (SecondaryId);
488 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
490 if (token == PrimaryId) {
491 string s = r.ReadString ();
495 return ctx.GetCache (r.ReadInt16 ());
498 protected override Type Type {
499 get { return typeof (string); }
502 protected override int NumberOfIds {
507 class IndexedStringFormatter : StringFormatter
509 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
511 IndexedString s = o as IndexedString;
514 throw new InvalidOperationException ("object is not of the IndexedString type");
516 base.Write (w, s.Value, ctx);
519 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
521 string s = base.Read (token, r, ctx) as string;
522 if (String.IsNullOrEmpty (s))
523 throw new InvalidOperationException ("string must not be null or empty.");
525 return new IndexedString (s);
528 protected override Type Type {
529 get { return typeof (IndexedString); }
532 protected override int NumberOfIds {
537 class Int64Formatter : ObjectFormatter
539 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
545 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
547 return r.ReadInt64 ();
549 protected override Type Type {
550 get { return typeof (long); }
554 class Int32Formatter : ObjectFormatter
556 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
559 if ((int)(byte) i == i) {
560 w.Write (SecondaryId);
568 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
570 if (token == PrimaryId)
571 return r.ReadInt32 ();
573 return (int) r.ReadByte ();
576 protected override Type Type {
577 get { return typeof (int); }
580 protected override int NumberOfIds {
585 class Int16Formatter : ObjectFormatter
587 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
593 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
595 return r.ReadInt16 ();
598 protected override Type Type {
599 get { return typeof (short); }
603 class ByteFormatter : ObjectFormatter
605 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
611 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
613 return r.ReadByte ();
616 protected override Type Type {
617 get { return typeof (byte); }
621 class BooleanFormatter : ObjectFormatter
623 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
628 w.Write (SecondaryId);
631 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
633 return token == PrimaryId;
636 protected override Type Type {
637 get { return typeof (bool); }
640 protected override int NumberOfIds {
645 class CharFormatter : ObjectFormatter
647 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
653 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
655 return r.ReadChar ();
658 protected override Type Type {
659 get { return typeof (char); }
663 class DateTimeFormatter : ObjectFormatter
665 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
668 w.Write (((DateTime) o).Ticks);
671 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
673 return new DateTime (r.ReadInt64 ());
676 protected override Type Type {
677 get { return typeof (DateTime); }
681 class PairFormatter : ObjectFormatter
683 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
687 WriteObject (w, p.First, ctx);
688 WriteObject (w, p.Second, ctx);
691 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
693 Pair p = new Pair ();
694 p.First = ReadObject (r, ctx);
695 p.Second = ReadObject (r, ctx);
699 protected override Type Type {
700 get { return typeof (Pair); }
704 class TripletFormatter : ObjectFormatter
706 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
708 Triplet t = (Triplet) o;
710 WriteObject (w, t.First, ctx);
711 WriteObject (w, t.Second, ctx);
712 WriteObject (w, t.Third, ctx);
715 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
717 Triplet t = new Triplet ();
718 t.First = ReadObject (r, ctx);
719 t.Second = ReadObject (r, ctx);
720 t.Third = ReadObject (r, ctx);
724 protected override Type Type {
725 get { return typeof (Triplet); }
729 class ArrayListFormatter : ObjectFormatter
731 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
733 ArrayList l = (ArrayList) o;
736 Write7BitEncodedInt (w, l.Count);
737 for (int i = 0; i < l.Count; i++)
738 WriteObject (w, l [i], ctx);
741 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
743 int len = Read7BitEncodedInt (r);
744 ArrayList l = new ArrayList (len);
746 for (int i = 0; i < len; i++)
747 l.Add (ReadObject (r, ctx));
752 protected override Type Type {
753 get { return typeof (ArrayList); }
757 class HashtableFormatter : ObjectFormatter
759 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
761 Hashtable ht = (Hashtable) o;
764 Write7BitEncodedInt (w, ht.Count);
765 foreach (DictionaryEntry de in ht) {
766 WriteObject (w, de.Key, ctx);
767 WriteObject (w, de.Value, ctx);
771 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
773 int len = Read7BitEncodedInt (r);
774 Hashtable ht = new Hashtable (len);
776 for (int i = 0; i < len; i++) {
777 object key = ReadObject (r, ctx);
778 object val = ReadObject (r, ctx);
786 protected override Type Type {
787 get { return typeof (Hashtable); }
791 class ObjectArrayFormatter : ObjectFormatter
793 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
795 object [] val = (object []) o;
798 Write7BitEncodedInt (w, val.Length);
799 for (int i = 0; i < val.Length; i++)
800 WriteObject (w, val [i], ctx);
803 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
805 int len = Read7BitEncodedInt (r);
806 object [] ret = new object [len];
808 for (int i = 0; i < len; i++)
809 ret [i] = ReadObject (r, ctx);
814 protected override Type Type {
815 get { return typeof (object []); }
821 #region System.Web Optimizations
822 class ColorFormatter : ObjectFormatter
824 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
828 if (c.IsEmpty || c.IsKnownColor) {
829 w.Write (SecondaryId);
831 w.Write (-1); //isempty marker
833 w.Write ((int) c.ToKnownColor ());
836 w.Write (c.ToArgb ());
840 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
842 int value = r.ReadInt32 ();
843 if (token == PrimaryId)
844 return Color.FromArgb (value);
846 if (value == -1) //isempty marker
848 return Color.FromKnownColor ((KnownColor)value);
852 protected override Type Type {
853 get { return typeof (Color); }
856 protected override int NumberOfIds {
863 #region Special Formatters
864 class EnumFormatter : ObjectFormatter
866 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
868 object value = Convert.ChangeType (o, ((Enum) o).GetTypeCode ());
870 WriteObject (w, o.GetType (), ctx);
871 WriteObject (w, value, ctx);
874 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
876 Type t = (Type) ReadObject (r, ctx);
877 object value = ReadObject (r, ctx);
879 return Enum.ToObject (t, value);
882 protected override Type Type {
883 get { return typeof (Enum); }
887 class TypeFormatter : ObjectFormatter
889 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
891 if (ctx.RegisterCache (o)) {
892 w.Write (SecondaryId);
896 w.Write (((Type) o).FullName);
898 // We should cache the name of the assembly
899 w.Write (((Type) o).Assembly.FullName);
903 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
905 if (token == PrimaryId) {
906 string type = r.ReadString ();
907 string assembly = r.ReadString ();
908 Type t = Assembly.Load (assembly).GetType (type);
912 return ctx.GetCache (r.ReadInt16 ());
916 protected override Type Type {
917 get { return typeof (Type); }
920 protected override int NumberOfIds {
925 class SingleRankArrayFormatter : ObjectFormatter
927 readonly BinaryFormatter _binaryFormatter = new BinaryFormatter ();
929 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
931 Array val = (Array) o;
932 if (val.GetType ().GetElementType ().IsPrimitive) {
933 w.Write (SecondaryId);
934 _binaryFormatter.Serialize (w.BaseStream, o);
939 WriteObject (w, val.GetType ().GetElementType (), ctx);
941 Write7BitEncodedInt (w, val.Length);
942 for (int i = 0; i < val.Length; i++)
943 WriteObject (w, val.GetValue (i), ctx);
946 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
948 if (token == SecondaryId)
949 return _binaryFormatter.Deserialize (r.BaseStream);
950 Type t = (Type) ReadObject (r, ctx);
951 int len = Read7BitEncodedInt (r);
952 Array val = Array.CreateInstance (t, len);
954 for (int i = 0; i < len; i++)
955 val.SetValue (ReadObject (r, ctx), i);
960 protected override Type Type {
961 get { return typeof (Array); }
964 protected override int NumberOfIds {
969 class FontUnitFormatter : StringFormatter
971 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
973 base.Write (w, o.ToString (), ctx);
976 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
978 return FontUnit.Parse ((string) base.Read (token, r, ctx));
981 protected override Type Type {
982 get { return typeof (FontUnit); }
986 class UnitFormatter : StringFormatter
988 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
990 base.Write (w, o.ToString (), ctx);
993 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
995 return Unit.Parse ((string) base.Read (token, r, ctx));
998 protected override Type Type {
999 get { return typeof (Unit); }
1003 class TypeConverterFormatter : StringFormatter
1005 TypeConverter converter;
1007 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
1009 w.Write (PrimaryId);
1010 ObjectFormatter.WriteObject (w, o.GetType (), ctx);
1011 string v = (string) converter.ConvertTo (null, Helpers.InvariantCulture,
1012 o, typeof (string));
1013 base.Write (w, v, ctx);
1016 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
1018 Type t = (Type) ObjectFormatter.ReadObject (r, ctx);
1019 converter = TypeDescriptor.GetConverter (t);
1020 token = r.ReadByte ();
1021 string v = (string) base.Read (token, r, ctx);
1022 return converter.ConvertFrom (null, Helpers.InvariantCulture, v);
1025 protected override Type Type {
1026 get { return typeof (TypeConverter); }
1029 public TypeConverter Converter {
1030 set { converter = value; }
1034 class BinaryObjectFormatter : ObjectFormatter
1036 protected override void Write (BinaryWriter w, object o, WriterContext ctx)
1038 w.Write (PrimaryId);
1040 MemoryStream ms = new MemoryStream (128);
1041 new BinaryFormatter ().Serialize (ms, o);
1043 byte [] buf = ms.GetBuffer ();
1044 Write7BitEncodedInt (w, buf.Length);
1045 w.Write (buf, 0, buf.Length);
1048 protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
1050 int len = Read7BitEncodedInt (r);
1051 byte [] buf = r.ReadBytes (len);
1052 if (buf.Length != len)
1053 throw new Exception ();
1055 return new BinaryFormatter ().Deserialize (new MemoryStream (buf));
1058 protected override Type Type {
1059 get { return typeof (object); }