1 //-----------------------------------------------------------------------------
2 // <copyright file="ContentTypeField.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //-----------------------------------------------------------------------------
7 namespace System.Net.Mime
10 using System.Collections;
11 using System.Collections.Specialized;
14 using System.Globalization;
15 using System.Net.Mail;
18 /// Typed Content-Type header
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.
32 public class ContentType
39 TrackingStringDictionary parameters;
42 /// Default content type - can be used if the Content-Type header
43 /// is not defined in the message headers.
45 internal readonly static string Default = "application/octet-stream";
47 public ContentType() : this(Default)
54 /// <param name="fieldValue">Unparsed value of the Content-Type header.</param>
55 public ContentType(string contentType)
57 if (contentType == null) {
58 throw new ArgumentNullException("contentType");
60 if (contentType == String.Empty) {
61 throw new ArgumentException(SR.GetString(SR.net_emptystringcall,"contentType"), "contentType");
68 public string Boundary
72 return Parameters["boundary"];
76 if (value == null || value == string.Empty) {
77 Parameters.Remove("boundary");
80 Parameters["boundary"] = value;
89 return Parameters["charset"];
93 if (value == null || value == string.Empty) {
94 Parameters.Remove("charset");
97 Parameters["charset"] = value;
103 /// Gets the media type.
105 public string MediaType
109 return mediaType + "/" + subType;
114 throw new ArgumentNullException("value");
117 if (value == string.Empty) {
118 throw new ArgumentException(SR.GetString(SR.net_emptystringset), "value");
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));
126 subType = MailBnfHelper.ReadToken(value, ref offset, null);
127 if(subType.Length == 0 || offset < value.Length){
128 throw new FormatException(SR.GetString(SR.MediaTypeInvalid));
139 string value = Parameters["name"];
140 Encoding nameEncoding = MimeBasePart.DecodeEncoding(value);
141 if(nameEncoding != null)
142 value = MimeBasePart.DecodeHeaderValue(value);
146 if (value == null || value == string.Empty) {
147 Parameters.Remove("name");
150 Parameters["name"] = value;
156 public StringDictionary Parameters
160 if (parameters == null)
163 parameters = new TrackingStringDictionary();
172 internal void Set(string contentType, HeaderCollection headers) {
175 headers.InternalSet(MailHeaderInfo.GetString(MailHeaderID.ContentType), ToString());
180 internal void PersistIfNeeded(HeaderCollection headers, bool forcePersist) {
181 if (IsChanged || !isPersisted || forcePersist) {
182 headers.InternalSet(MailHeaderInfo.GetString(MailHeaderID.ContentType), ToString());
187 internal bool IsChanged {
189 return (isChanged || parameters != null && parameters.IsChanged);
193 public override string ToString() {
194 if (type == null || IsChanged)
196 type = Encode(false); // Legacy wire-safe format
198 parameters.IsChanged = false;
204 internal string Encode(bool allowUnicode)
206 StringBuilder builder = new StringBuilder();
207 builder.Append(mediaType); // Must not have unicode, already validated
209 builder.Append(subType); // Must not have unicode, already validated
210 // Validate and encode unicode where required
211 foreach (string key in Parameters.Keys)
213 builder.Append("; ");
214 EncodeToBuffer(key, builder, allowUnicode);
216 EncodeToBuffer(parameters[key], builder, allowUnicode);
218 return builder.ToString();
221 private static void EncodeToBuffer(string value, StringBuilder builder, bool allowUnicode)
223 Encoding encoding = MimeBasePart.DecodeEncoding(value);
224 if (encoding != null) // Manually encoded elsewhere, pass through
226 builder.Append("\"" + value + "\"");
228 else if ((allowUnicode && !MailBnfHelper.HasCROrLF(value)) // Unicode without CL or LF's
229 || MimeBasePart.IsAscii(value, false)) // Ascii
231 MailBnfHelper.GetTokenOrQuotedString(value, builder, allowUnicode);
235 // MIME Encoding required
236 encoding =Encoding.GetEncoding(MimeBasePart.defaultCharSet);
237 builder.Append("\"" + MimeBasePart.EncodeHeaderValue(value, encoding,
238 MimeBasePart.ShouldUseBase64Encoding(encoding)) + "\"");
242 public override bool Equals(object rparam) {
243 if (rparam == null) {
247 return (String.Compare(ToString(), rparam.ToString(), StringComparison.OrdinalIgnoreCase ) == 0);
250 public override int GetHashCode(){
251 return ToString().ToLowerInvariant().GetHashCode();
259 Exception exception = null;
260 parameters = new TrackingStringDictionary();
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));
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));
275 if (exception == null) {
276 while (MailBnfHelper.SkipCFWS(type, ref offset))
278 if (type[offset++] != ';'){
279 exception = new FormatException(SR.GetString(SR.ContentTypeInvalid));
283 if (!MailBnfHelper.SkipCFWS(type, ref offset))
286 string paramAttribute = MailBnfHelper.ReadParameterAttribute(type, ref offset, null);
288 if(paramAttribute == null || paramAttribute.Length == 0){
289 exception = new FormatException(SR.GetString(SR.ContentTypeInvalid));
294 if ( offset >= type.Length || type[offset++] != '='){
295 exception = new FormatException(SR.GetString(SR.ContentTypeInvalid));
299 if (!MailBnfHelper.SkipCFWS(type, ref offset)){
300 exception = new FormatException(SR.GetString(SR.ContentTypeInvalid));
304 if (type[offset] == '"')
305 paramValue = MailBnfHelper.ReadQuotedString(type, ref offset, null);
307 paramValue = MailBnfHelper.ReadToken(type, ref offset, null);
309 if(paramValue == null){
310 exception = new FormatException(SR.GetString(SR.ContentTypeInvalid));
314 parameters.Add(paramAttribute, paramValue);
317 parameters.IsChanged = false;
319 catch(FormatException){
320 throw new FormatException(SR.GetString(SR.ContentTypeInvalid));
323 if(exception != null){
324 throw new FormatException(SR.GetString(SR.ContentTypeInvalid));