Adding reference source for System.Net
[mono.git] / mcs / class / referencesource / System / net / System / Net / mail / ContentType.cs
1 //-----------------------------------------------------------------------------
2 // <copyright file="ContentTypeField.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //-----------------------------------------------------------------------------
6
7 namespace System.Net.Mime
8 {
9     using System;
10     using System.Collections;
11     using System.Collections.Specialized;
12     using System.IO;
13     using System.Text;
14     using System.Globalization;
15     using System.Net.Mail;
16
17     /// <summary>
18     /// Typed Content-Type header
19     ///
20     /// We parse the type during construction and set.
21     /// null and string.empty will throw for construction,set and mediatype/subtype
22     /// constructors set isPersisted to false.  isPersisted needs to be tracked seperately
23     /// than isChanged because isChanged only determines if the cached value should be used.
24     /// isPersisted tracks if the object has been persisted. However, obviously if isChanged is true
25     /// the object isn't  persisted.
26     /// If any subcomponents are changed, isChanged is set to true and isPersisted is false
27     /// ToString caches the value until a isChanged is true, then it recomputes the full value.
28     /// </summary>
29
30
31
32     public class ContentType
33     {
34         string mediaType;
35         string subType;
36         bool isChanged;
37         string type;
38         bool isPersisted;
39         TrackingStringDictionary parameters;
40
41         /// <summary>
42         /// Default content type - can be used if the Content-Type header
43         /// is not defined in the message headers.
44         /// </summary>
45         internal readonly static string Default = "application/octet-stream";
46
47         public ContentType() : this(Default)
48         {
49         }
50
51         /// <summary>
52         /// ctor.
53         /// </summary>
54         /// <param name="fieldValue">Unparsed value of the Content-Type header.</param>
55         public ContentType(string contentType)
56         {
57             if (contentType == null) {
58                 throw new ArgumentNullException("contentType");
59             }
60             if (contentType == String.Empty) {
61                 throw new ArgumentException(SR.GetString(SR.net_emptystringcall,"contentType"), "contentType");
62             }                
63             isChanged = true;
64             type = contentType;
65             ParseValue();
66         }
67
68         public string Boundary
69         {
70             get
71             {
72                 return Parameters["boundary"];
73             }
74             set
75             {
76                 if (value == null || value == string.Empty) {
77                     Parameters.Remove("boundary");
78                 }
79                 else{
80                     Parameters["boundary"] = value;
81                 }
82             }
83         }
84
85         public string CharSet
86         {
87             get
88             {
89                 return Parameters["charset"];
90             }
91             set
92             {
93                 if (value == null || value == string.Empty) {
94                     Parameters.Remove("charset");
95                 }
96                 else{
97                     Parameters["charset"] = value;
98                 }
99             }
100         }
101
102         /// <summary>
103         /// Gets the media type.
104         /// </summary>
105         public string MediaType
106         {
107             get
108             {
109                 return mediaType + "/" + subType;
110             }
111             set
112             {
113                 if (value == null) {
114                     throw new ArgumentNullException("value");
115                 }
116                 
117                 if (value == string.Empty) {
118                     throw new ArgumentException(SR.GetString(SR.net_emptystringset), "value");
119                 }
120
121                 int offset = 0;
122                 mediaType = MailBnfHelper.ReadToken(value, ref offset, null);
123                 if (mediaType.Length == 0 || offset >= value.Length || value[offset++] != '/')
124                     throw new FormatException(SR.GetString(SR.MediaTypeInvalid));
125
126                 subType = MailBnfHelper.ReadToken(value, ref offset, null);
127                 if(subType.Length == 0 || offset < value.Length){
128                     throw new FormatException(SR.GetString(SR.MediaTypeInvalid));
129                 }
130
131                 isChanged = true;
132                 isPersisted = false;
133             }
134         }
135
136
137         public string Name {
138             get {
139                 string value = Parameters["name"];
140                 Encoding nameEncoding = MimeBasePart.DecodeEncoding(value);
141                 if(nameEncoding != null)
142                     value = MimeBasePart.DecodeHeaderValue(value);
143                 return value;
144             }
145             set {
146                 if (value == null || value == string.Empty) {
147                     Parameters.Remove("name");
148                 }
149                 else{
150                     Parameters["name"] = value;
151                 }
152             }
153         }
154         
155
156         public StringDictionary Parameters
157         {
158             get
159             {
160                 if (parameters == null)
161                 {
162                     if (type == null) {
163                         parameters = new TrackingStringDictionary();
164                     }
165                 }
166                 return parameters;
167             }
168         }
169
170
171         
172         internal void Set(string contentType, HeaderCollection headers) {
173             type = contentType;
174             ParseValue();
175             headers.InternalSet(MailHeaderInfo.GetString(MailHeaderID.ContentType), ToString());
176             isPersisted = true;
177         }
178         
179         
180         internal void PersistIfNeeded(HeaderCollection headers, bool forcePersist) {
181             if (IsChanged || !isPersisted || forcePersist) {
182                 headers.InternalSet(MailHeaderInfo.GetString(MailHeaderID.ContentType), ToString());
183                 isPersisted = true;
184             }
185         }
186
187         internal bool IsChanged {
188             get {
189                 return (isChanged || parameters != null && parameters.IsChanged);
190             }
191         }
192
193         public override string ToString() {
194             if (type == null || IsChanged)
195             {
196                 type = Encode(false); // Legacy wire-safe format
197                 isChanged = false;
198                 parameters.IsChanged = false;
199                 isPersisted = false;
200             }
201             return type;
202         }
203
204         internal string Encode(bool allowUnicode)
205         {
206             StringBuilder builder = new StringBuilder();
207             builder.Append(mediaType); // Must not have unicode, already validated
208             builder.Append('/');
209             builder.Append(subType);  // Must not have unicode, already validated
210             // Validate and encode unicode where required
211             foreach (string key in Parameters.Keys)
212             {
213                 builder.Append("; ");
214                 EncodeToBuffer(key, builder, allowUnicode);
215                 builder.Append('=');
216                 EncodeToBuffer(parameters[key], builder, allowUnicode);
217             }
218             return builder.ToString();
219         }
220
221         private static void EncodeToBuffer(string value, StringBuilder builder, bool allowUnicode)
222         {
223             Encoding encoding = MimeBasePart.DecodeEncoding(value);
224             if (encoding != null) // Manually encoded elsewhere, pass through
225             {
226                 builder.Append("\"" + value + "\"");
227             } 
228             else if ((allowUnicode && !MailBnfHelper.HasCROrLF(value)) // Unicode without CL or LF's
229                 || MimeBasePart.IsAscii(value, false)) // Ascii
230             {
231                 MailBnfHelper.GetTokenOrQuotedString(value, builder, allowUnicode);
232             }
233             else
234             {
235                 // MIME Encoding required
236                 encoding =Encoding.GetEncoding(MimeBasePart.defaultCharSet);
237                 builder.Append("\"" + MimeBasePart.EncodeHeaderValue(value, encoding,
238                     MimeBasePart.ShouldUseBase64Encoding(encoding)) + "\"");
239             }
240         }
241
242         public override bool Equals(object rparam) {
243             if (rparam == null) {
244                 return false;
245             }
246             
247             return (String.Compare(ToString(), rparam.ToString(), StringComparison.OrdinalIgnoreCase ) == 0);
248         }
249
250         public override int GetHashCode(){
251             return ToString().ToLowerInvariant().GetHashCode();
252         }
253
254         // Helper methods.
255
256         void ParseValue()
257         {
258             int offset = 0;
259             Exception exception = null;
260             parameters = new TrackingStringDictionary();
261             
262             try{
263                 mediaType = MailBnfHelper.ReadToken(type, ref offset, null);
264                 if (mediaType == null || mediaType.Length == 0 ||  offset >= type.Length || type[offset++] != '/'){
265                     exception = new FormatException(SR.GetString(SR.ContentTypeInvalid));
266                 }
267
268                 if (exception == null) {
269                     subType = MailBnfHelper.ReadToken(type, ref offset, null);
270                     if (subType == null || subType.Length == 0){
271                         exception = new FormatException(SR.GetString(SR.ContentTypeInvalid));
272                     }
273                 }
274                 
275                 if (exception == null) {
276                     while (MailBnfHelper.SkipCFWS(type, ref offset))
277                     {
278                         if (type[offset++] != ';'){
279                             exception = new FormatException(SR.GetString(SR.ContentTypeInvalid));
280                             break;
281                         }
282         
283                         if (!MailBnfHelper.SkipCFWS(type, ref offset))
284                             break;
285         
286                         string paramAttribute = MailBnfHelper.ReadParameterAttribute(type, ref offset, null);
287                         
288                         if(paramAttribute == null || paramAttribute.Length == 0){
289                             exception = new FormatException(SR.GetString(SR.ContentTypeInvalid));
290                             break;
291                         }
292                         
293                         string paramValue;
294                         if ( offset >= type.Length || type[offset++] != '='){
295                             exception = new FormatException(SR.GetString(SR.ContentTypeInvalid));
296                             break;
297                         }
298                         
299                         if (!MailBnfHelper.SkipCFWS(type, ref offset)){
300                             exception = new FormatException(SR.GetString(SR.ContentTypeInvalid));
301                             break;
302                         }
303         
304                         if (type[offset] == '"')
305                             paramValue = MailBnfHelper.ReadQuotedString(type, ref offset, null);
306                         else
307                             paramValue = MailBnfHelper.ReadToken(type, ref offset, null);
308                         
309                         if(paramValue == null){
310                             exception = new FormatException(SR.GetString(SR.ContentTypeInvalid));
311                             break;
312                         }
313         
314                         parameters.Add(paramAttribute, paramValue);
315                     }
316                 }
317                 parameters.IsChanged = false;
318             }
319             catch(FormatException){
320                 throw new FormatException(SR.GetString(SR.ContentTypeInvalid));
321             }
322
323             if(exception != null){
324                 throw new FormatException(SR.GetString(SR.ContentTypeInvalid));
325             }
326         }
327     }
328 }