Merge pull request #2310 from lambdageek/dev/bug-36305
[mono.git] / mcs / class / System / System.Net.Mime / ContentDisposition.cs
1 //
2 // System.Net.Mime.ContentDisposition.cs
3 //
4 // Authors:
5 //      Tim Coleman (tim@timcoleman.com)
6 //      John Luke (john.luke@gmail.com)
7 //
8 // Copyright (C) Tim Coleman, 2004
9 // Copyright (C) John Luke, 2005
10 //
11
12 //
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:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
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.
31 //
32
33 using System.Text;
34 using System.Collections;
35 using System.Collections.Specialized;
36 using System.Globalization;
37
38 namespace System.Net.Mime {
39         public class ContentDisposition
40         {
41                 // FIXME: "r" was not enough, neither was zzz
42                 // so this will fail if the offset is not an even hour
43                 const string rfc822 = "dd MMM yyyy HH':'mm':'ss zz00";
44                 
45                 #region Fields
46
47                 string dispositionType;
48                 StringDictionary parameters = new StringDictionary ();
49
50                 #endregion // Fields
51
52                 #region Constructors
53
54                 public ContentDisposition () : this (DispositionTypeNames.Attachment)
55                 {
56                 }
57
58                 public ContentDisposition (string disposition)
59                 {
60                         if (disposition == null)
61                                 throw new ArgumentNullException ();
62                         if (disposition.Length < 1)
63                                 throw new FormatException ();
64                         Size = -1;
65
66                         try {
67                                 int index = disposition.IndexOf (';');
68                                 if (index < 0) {
69                                         dispositionType = disposition.Trim ();
70                                 }
71                                 else {
72                                         string[] split = disposition.Split (';');
73                                         dispositionType = split[0].Trim ();
74                                         for (int i = 1; i < split.Length; i++)
75                                                 Parse (split[i]);
76                                 }
77                         } catch {
78                                 throw new FormatException ();
79                         }
80                 }
81
82                 // the individual pieces
83                 void Parse (string pair)
84                 {
85                         if (pair == null || pair.Length < 0)
86                                 return;
87
88                         string[] split = pair.Split ('=');
89                         if (split.Length == 2)
90                                 parameters.Add (split[0].Trim (), split[1].Trim ());
91                         else
92                                 throw new FormatException ();
93                 }
94
95                 #endregion // Constructors
96
97                 #region Properties
98
99                 public DateTime CreationDate {
100                         get {
101                                 if (parameters.ContainsKey ("creation-date"))
102                                         return DateTime.ParseExact (parameters["creation-date"], rfc822, null);
103                                 else
104                                         return DateTime.MinValue;
105                         }
106                         set {
107                                 if (value > DateTime.MinValue)
108                                         parameters["creation-date"] = value.ToString (rfc822);
109                                 else
110                                         parameters.Remove ("modification-date");
111                         }
112                 }
113
114                 public string DispositionType {
115                         get { return dispositionType; }
116                         set {
117                                 if (value == null)
118                                         throw new ArgumentNullException ();
119                                 if (value.Length < 1)
120                                         throw new ArgumentException ();
121                                 dispositionType = value;
122                         }
123                 }
124
125                 public string FileName {
126                         get { return parameters["filename"]; }
127                         set { parameters["filename"] = value; }
128                 }
129
130                 public bool Inline {
131                         get { return String.Compare (dispositionType, DispositionTypeNames.Inline, true, CultureInfo.InvariantCulture) == 0; }
132                         set {
133                                 if (value)
134                                         dispositionType = DispositionTypeNames.Inline;
135                                 else
136                                         dispositionType = DispositionTypeNames.Attachment;
137                         }
138                 }
139
140                 public DateTime ModificationDate {
141                         get {
142                                 if (parameters.ContainsKey ("modification-date"))
143                                         return DateTime.ParseExact (parameters["modification-date"], rfc822, null);
144                                 else
145                                         return DateTime.MinValue;
146                         }
147                         set {
148                                 if (value > DateTime.MinValue)
149                                         parameters["modification-date"] = value.ToString (rfc822);
150                                 else
151                                         parameters.Remove ("modification-date");
152                         }
153                 }
154
155                 public StringDictionary Parameters {
156                         get { return parameters; }
157                 }
158
159                 public DateTime ReadDate {
160                         get {
161                                 if (parameters.ContainsKey ("read-date"))
162                                         return DateTime.ParseExact (parameters["read-date"], rfc822, null);
163                                 else
164                                         return DateTime.MinValue;
165                         }
166                         set {
167                                 if (value > DateTime.MinValue)
168                                         parameters["read-date"] = value.ToString (rfc822);
169                                 else
170                                         parameters.Remove ("read-date");
171                         }
172                 }
173
174                 public long Size {
175                         get {
176                                 if (parameters.ContainsKey ("size"))
177                                         return long.Parse (parameters["size"]);
178                                 else
179                                         return -1;
180                         }
181                         set {
182                                 if (value > -1)
183                                         parameters["size"] = value.ToString ();
184                                 else
185                                         parameters.Remove ("size");
186                         }
187                 }
188
189                 #endregion // Properties
190
191                 #region Methods
192
193                 public override bool Equals (object obj)
194                 {
195                         return Equals (obj as ContentDisposition);
196                 }
197
198                 bool Equals (ContentDisposition other)
199                 {
200                         return other != null && ToString () == other.ToString ();
201                 }
202
203                 public override int GetHashCode ()
204                 {
205                         return ToString ().GetHashCode ();
206                 }
207
208                 public override string ToString ()
209                 {
210                         // the content-disposition header as in RFC 2183
211                         // ex. attachment; filename=genome.jpeg; modification-date="Wed, 12 Feb 1997 16:29:51 -0500";
212                         // the dates must be quoted and in RFC 822 format
213                         //
214                         // According to RFC 2183, the filename field value follows the definition
215                         // given in RFC 1521, which is
216                         //
217                         //  value := token / quoted-string
218                         //
219                         StringBuilder sb = new StringBuilder ();
220                         sb.Append (DispositionType.ToLower ());
221                         if (Parameters != null && Parameters.Count > 0) {
222                                 bool quote = false;
223                                 string key, value;
224                                 
225                                 foreach (DictionaryEntry pair in Parameters)
226                                 {
227                                         if (pair.Value != null && pair.Value.ToString ().Length > 0) {
228                                                 sb.Append ("; ");
229                                                 sb.Append (pair.Key);
230                                                 sb.Append ("=");
231
232                                                 key = pair.Key.ToString ();
233                                                 value = pair.Value.ToString ();
234                                                 if ((key == "filename" && value.IndexOf (' ') != -1) || key.EndsWith ("date"))
235                                                         quote = true;
236                                                 else
237                                                         quote = false;
238                                                 
239                                                 if (quote)
240                                                         sb.Append ("\"");
241                                                 sb.Append (value);
242                                                 if (quote)
243                                                         sb.Append ("\"");
244                                         }
245                                 }
246                         }
247                         return sb.ToString ();
248                 }
249
250                 #endregion // Methods
251         }
252 }
253