From 8422f1755e9b73d7b625a825bfe207060a2a83c1 Mon Sep 17 00:00:00 2001 From: CaS Date: Tue, 18 Dec 2001 17:46:13 +0000 Subject: [PATCH] Update GSMime documentation git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@11815 72102866-910b-0410-8b05-ffd578937521 --- Headers/gnustep/base/GSMime.h | 2 +- Source/GSMime.m | 412 ++++++++++++++++++++++++++++++++-- Tools/AGSHtml.m | 47 +++- 3 files changed, 430 insertions(+), 31 deletions(-) diff --git a/Headers/gnustep/base/GSMime.h b/Headers/gnustep/base/GSMime.h index 3c474fdde..a653c7386 100644 --- a/Headers/gnustep/base/GSMime.h +++ b/Headers/gnustep/base/GSMime.h @@ -107,7 +107,7 @@ - (BOOL) parsedHeaders; - (BOOL) scanHeader: (NSScanner*)scanner named: (NSString*)name - inTo: (NSMutableDictionary*)info; + into: (NSMutableDictionary*)info; - (BOOL) scanPastSpace: (NSScanner*)scanner; - (NSString*) scanSpecial: (NSScanner*)scanner; - (NSString*) scanToken: (NSScanner*)scanner; diff --git a/Source/GSMime.m b/Source/GSMime.m index 9109a9f04..859c59b31 100644 --- a/Source/GSMime.m +++ b/Source/GSMime.m @@ -1,6 +1,6 @@ /** Implementation for GSMIME - Copyright (C) 2000 Free Software Foundation, Inc. + Copyright (C) 2000,2001 Free Software Foundation, Inc. Written by: Richard frith-Macdonald Date: October 2000 @@ -20,6 +20,37 @@ You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. + + The MIME parsing system + + Mime Parser +

+ The GNUstep Mime parser. This is collection Objective-C classes + for representing MIME (and HTTP) documents and managing conversions + to and from convenient internal formats. +

+

+ Eventually the goal is to center round three classes - +

+ + document + + A container for the actual data (and headers) of a mime/http document. + parser + + An object that can be fed data and will parse it into a document. + This object also provides various utility methods and an API + that permits overriding in order to extend the functionality to + cope with new document types. + + unparser + + An object to take a mime/http document and produce a data object + suitable for transmission. + + +
+ $Date$ $Revision$ */ #include @@ -299,28 +330,51 @@ parseCharacterSet(NSString *token) - (BOOL) _unfoldHeader; @end +/** + *

+ * This class provides support for parsing MIME messages + * into GSMimeDocument objects. Each parser object maintains + * an associated document into which data is stored. + *

+ *

+ * You supply the document to be parsed as one or more data + * items passed to the Parse: method, and (if + * the method always returns YES, you give it + * a final nil argument to mark the end of the + * document. + *

+ *

+ * On completion of parsing a valid document, the + * document method returns the resulting parsed document. + *

+ */ @implementation GSMimeParser +/** + * Create and return a parser. + */ + (GSMimeParser*) mimeParser { return AUTORELEASE([[self alloc] init]); } -- (NSData*) data -{ - return data; -} - -- (void) dealloc -{ - RELEASE(data); - RELEASE(child); - RELEASE(context); - RELEASE(boundary); - RELEASE(document); - [super dealloc]; -} - +/** + * Return a coding context object to be used for decoding data + * according to the scheme specified in the header. + *

+ * The default implementation supports the following transfer + * encodings specified in either a transfer-encoding + * of content-transfer-encoding header - + *

+ * + * base64 + * quoted-printable + * binary + * 7bit + * 8bit + * chunked (for HTTP/1.1) + * + */ - (GSMimeCodingContext*) contextFor: (NSDictionary*)info { NSString *name; @@ -371,6 +425,47 @@ parseCharacterSet(NSString *token) return AUTORELEASE([GSMimeBinaryDecoderContext new]); } +/** + * Return the data accumulated in the parser. If the parser is + * still parsing headers, this will be the header data read so far. + * If the parse has parsed the body of the message, this will be + * the data of the body, with any transfer encoding removed. + */ +- (NSData*) data +{ + return data; +} + +- (void) dealloc +{ + RELEASE(data); + RELEASE(child); + RELEASE(context); + RELEASE(boundary); + RELEASE(document); + [super dealloc]; +} + +/** + *

+ * Decodes the raw data from the specified range in the source + * data object and appends it to the destination data object. + * The context object provides information about the content + * encoding type in use, and the state of the decoding operation. + *

+ *

+ * This method may be called repeatedly to incrementally decode + * information as it arrives on some communications channel. + * It should be called with a nil source data item (or with + * the atEnd flag of the context set to YES) in order to flush + * any information held in the context to the output data + * object. + *

+ *

+ * You may override this method in order to implement + * additional coding schemes. + *

+ */ - (BOOL) decodeData: (NSData*)sData fromRange: (NSRange)aRange intoData: (NSMutableData*)dData @@ -768,21 +863,35 @@ parseCharacterSet(NSString *token) return desc; } +/** + * Returns the object into which raw mime data is being parsed. + */ - (GSMimeDocument*) document { return document; } +/** + * Returns YES if the document parsing is known to be completed. + */ - (BOOL) isComplete { return complete; } +/** + * Returns YES if all the document headers have been parsed but + * the document body parsing may not yet be complete. + */ - (BOOL) isInBody { return inBody; } +/** + * Returns YES if parsing of the document headers has not yet + * been completed. + */ - (BOOL) isInHeaders { if (inBody == YES) @@ -803,6 +912,22 @@ parseCharacterSet(NSString *token) return self; } +/** + *

+ * This method is called repeatedly to pass raw mime data into + * the parser. It returns YES as long as it wants + * more data to complete parsing of a document, and NO + * if parsing is complete, either due to having reached the end of + * a document or due to an error. + *

+ *

+ * Since it is not always possible to determine if the end of a + * MIME document has been reached from its content, the method + * may need to be called with a nil argument after you have + * passed all the data to it ... this tells it that the data + * is complete. + *

+ */ - (BOOL) parse: (NSData*)d { if (complete == YES) @@ -913,6 +1038,42 @@ parseCharacterSet(NSString *token) } } +/** + *

+ * This method is called to parse a header line for the + * current document, split its contents into an info + * dictionary, and add that information to the document. + *

+ *

+ * The standard implementation of this method scans basic + * information and then calls -scanHeader:named:into: + * to complete the parsing of the header. + *

+ *

+ * This method also performs consistency checks on headers scanned + * so it is recommended that it is not overridden, but that + * subclasses override -scanHeader:named:into: to + * implement custom scanning. + *

+ *

+ * As a special case, for HTTP support, this method also parses + * lines in the format of HTTP responses as if they were headers + * named http. The resulting header info dictionary + * contains - + *

+ * + * HttpMajorVersion + * The first part of the version number + * HttpMinorVersion + * The second part of the version number + * NSHTTPPropertyServerHTTPVersionKey + * The full HTTP protocol version number + * NSHTTPPropertyStatusCodeKey + * The HTTP status code + * NSHTTPPropertyStatusReasonKey + * The text message (if any) after the status code + * + */ - (BOOL) parseHeader: (NSString*)aHeader { NSScanner *scanner = [NSScanner scannerWithString: aHeader]; @@ -973,7 +1134,7 @@ parseCharacterSet(NSString *token) /* * Break header fields out into info dictionary. */ - if ([self scanHeader: scanner named: name inTo: info] == NO) + if ([self scanHeader: scanner named: name into: info] == NO) { return NO; } @@ -1079,18 +1240,111 @@ parseCharacterSet(NSString *token) return [document addHeader: info]; } +/** + * Returns YES if the parser is expecting to read mime headers, + * Returns NO is the parser has already been passed all the + * data containing headers, and is now waiting for the body of + * the mime message (or has been passed all data). + */ - (BOOL) parsedHeaders { return inBody; } -/* - * Parse an unloaded and decoded header line, splitting information - * into an 'info' dictionary. +/** + *

+ * This method is called to parse a header line and split its + * contents into an info dictionary. + *

+ *

+ * On entry, the dictionary is already partially filled, + * the name argument is a lowercase representation of the + * header name, and the scanner is set to a scan location + * immediately after the colon in the header string. + *

+ *

+ * If the header is parsed successfully, the method should + * return YES, otherwise NO. + *

+ *

+ * You should not call this method directly yourself, but may + * override it to support parsing of new headers. + *

+ *

+ * You should be aware of the parsing that the standard + * implementation performs, and that needs to be + * done for certain headers in order to permit the parser to + * work generally - + *

+ * +* +* content-disposition +* +* +* Parameters +* +* A dictionary containing parameters as key-value pairs +* in lowercase +* +* Value +* +* The content disposition (excluding parameters) as a +* lowercase string. +* +* +* +* content-type +* +* +* Parameters +* +* A dictionary containing parameters as key-value pairs +* in lowercase. +* +* SubType +* The MIME subtype lowercase +* Type +* The MIME type lowercase +* value +* The full MIME type (xxx/yyy) in lowercase +* +* +* +* content-transfer-encoding +* +* +* Value +* The transfer encoding type in lowercase +* +* +* +* http +* +* +* HttpVersion +* The HTTP protocol version number +* HttpMajorVersion +* The first component of the version number +* HttpMinorVersion +* The second component of the version number +* HttpStatus +* The response status value (numeric code) +* Value +* The text message (if any) +* +* +* transfer-encoding +* +* +* Value +* The transfer encoding type in lowercase +* +* + * */ - (BOOL) scanHeader: (NSScanner*)scanner named: (NSString*)name - inTo: (NSMutableDictionary*)info + into: (NSMutableDictionary*)info { NSString *value = nil; NSMutableDictionary *parameters = nil; @@ -1289,6 +1543,11 @@ parseCharacterSet(NSString *token) return YES; } +/** + * A convenience method to scan past any whitespace in the scanner + * in preparation for scanning something more interesting that + * comes after it. Returns YES if any space was read, NO otherwise. + */ - (BOOL) scanPastSpace: (NSScanner*)scanner { NSCharacterSet *skip; @@ -1302,6 +1561,13 @@ parseCharacterSet(NSString *token) return scanned; } +/** + * A convenience method to use a scanner (that is set up to scan a + * header line) to scan in a special character that terminated a + * token previously scanned. If the token was terminated by + * whitespace and no other special character, the string returned + * will contain a single space character. + */ - (NSString*) scanSpecial: (NSScanner*)scanner { unsigned location; @@ -1326,8 +1592,14 @@ parseCharacterSet(NSString *token) } } -/* - * Get a mime field value - token or quoted string. +/** + * A convenience method to use a scanner (that is set up to scan a + * header line) to scan a header token - either a quoted string or + * a simple word. + * + * Leading whitespace is ignored. + * Backslash escapes in quoted text are converted + * */ - (NSString*) scanToken: (NSScanner*)scanner { @@ -1895,6 +2167,44 @@ parseCharacterSet(NSString *token) +/** + *

+ * This class is intended to provide a wrapper for MIME messages + * permitting easy access to the contents of a message and + * providing a basis for parsing an unparsing messages that + * have arrived via email or as a web document. + *

+ *

+ * The class keeps track of all the document headers, and provides + * methods for modifying the headers that apply to a document and + * for looking at the header structures, by providing an info + * dictionary containing the various parts of a header. + *

+ *

+ * The common dictionary keys used for elements provided for + * all headers are - + *

+ * + * RawHeader + * This is the unmodified text of the header + * + * BaseName + * This is the header name. + * + * BaseValue + * This is the text after the header name and colon. + * + * Name + * This is a lowercase representation of the header name. + * + * Value + * This is the value of the header (normally lower case). + * It may only be a small subset of the information in the header + * with other information being split into separate fields + * depending on the type of header. + * + * + */ @implementation GSMimeDocument + (void) initialize @@ -1916,11 +2226,21 @@ parseCharacterSet(NSString *token) } } +/** + * Create an empty MIME document. + */ + (GSMimeDocument*) mimeDocument { return AUTORELEASE([[self alloc] init]); } +/** + *

+ * This method may be called to add a header to the document. + * The header must be a mutable dictionary object that contains + * at least the fields that are standard for all headers. + *

+ */ - (BOOL) addHeader: (NSDictionary*)info { NSString *name = [info objectForKey: @"Name"]; @@ -1937,11 +2257,33 @@ parseCharacterSet(NSString *token) return YES; } +/** + *

+ * This method returns an array containing NSDictionary objects + * representing the headers associated with the document. + *

+ *

+ * The order of the headers in the array is the order of the + * headers in the document. + *

+ */ - (NSArray*) allHeaders { return [NSArray arrayWithArray: headers]; } +/** + * This returns the content data of the document in the + * appropriate format for the type of data - + * + * text + * an NSString object + * binary + * an NSData object + * multipart + * an NSArray object containing GSMimeDocument objects + * + */ - (id) content { return content; @@ -1959,6 +2301,10 @@ parseCharacterSet(NSString *token) [super dealloc]; } +/** + * This method removes all occurrances of headers whose raw data + * exactly matches the supplied string. + */ - (void) deleteHeader: (NSString*)aHeader { unsigned count = [headers count]; @@ -1974,6 +2320,10 @@ parseCharacterSet(NSString *token) } } +/** + * This method removes all occurrances of headers whose name + * exactly matches the supplied string. + */ - (void) deleteHeaderNamed: (NSString*)name { unsigned count = [headers count]; @@ -2002,6 +2352,10 @@ parseCharacterSet(NSString *token) return desc; } +/** + * This method returns the info dictionary for the first header + * whose name equals the supplied argument. + */ - (NSDictionary*) headerNamed: (NSString*)name { unsigned count = [headers count]; @@ -2021,6 +2375,10 @@ parseCharacterSet(NSString *token) return nil; } +/** + * This method returns an array of info dictionaries for all headers + * whose names equal the supplied argument. + */ - (NSArray*) headersNamed: (NSString*)name { unsigned count = [headers count]; @@ -2051,12 +2409,20 @@ parseCharacterSet(NSString *token) return self; } +/** + * Sets a new value for the content of the document. + */ - (BOOL) setContent: (id)newContent { ASSIGN(content, newContent); return YES; } +/** + * This method may be called to set a header in the document. + * Any other headers with the same name will be removed from + * the document. + */ - (BOOL) setHeader: (NSDictionary*)info { NSString *name = [info objectForKey: @"Name"]; diff --git a/Tools/AGSHtml.m b/Tools/AGSHtml.m index 92dd3beb6..d74755cde 100644 --- a/Tools/AGSHtml.m +++ b/Tools/AGSHtml.m @@ -493,7 +493,10 @@ static NSMutableSet *textNodes = nil; [buf appendString: indent]; [buf appendString: @"

\n"]; [self incIndent]; - [self outputBlock: children to: buf inPara: YES]; + while (children != nil) + { + children = [self outputBlock: children to: buf inPara: YES]; + } [self decIndent]; [buf appendString: indent]; [buf appendString: @"

\n"]; @@ -693,7 +696,10 @@ static NSMutableSet *textNodes = nil; if (desc != nil) { [self incIndent]; - [self outputBlock: desc to: buf inPara: NO]; + while (desc != nil) + { + desc = [self outputBlock: desc to: buf inPara: NO]; + } [self decIndent]; } [buf appendString: indent]; @@ -720,10 +726,15 @@ static NSMutableSet *textNodes = nil; } if ([[children name] isEqual: @"abstract"] == YES) { + GSXMLNode *tmp = [children children]; + [buf appendString: indent]; [buf appendString: @"
\n"]; [self incIndent]; - [self outputBlock: [children children] to: buf inPara: NO]; + while (tmp != nil) + { + tmp = [self outputBlock: tmp to: buf inPara: NO]; + } [self decIndent]; [buf appendString: indent]; [buf appendString: @"
\n"]; @@ -1285,10 +1296,15 @@ NSLog(@"Element '%@' not implemented", name); // FIXME [self incIndent]; while (children != nil) { + GSXMLNode *tmp = [children children]; + [buf appendString: indent]; [buf appendString: @"
  • \n"]; [self incIndent]; - [self outputBlock: [children children] to: buf inPara: NO]; + while (tmp != nil) + { + tmp = [self outputBlock: tmp to: buf inPara: NO]; + } [self decIndent]; [buf appendString: indent]; [buf appendString: @"
  • \n"]; @@ -1305,10 +1321,15 @@ NSLog(@"Element '%@' not implemented", name); // FIXME [self incIndent]; while (children != nil) { + GSXMLNode *tmp = [children children]; + [buf appendString: indent]; [buf appendString: @"
  • \n"]; [self incIndent]; - [self outputBlock: [children children] to: buf inPara: NO]; + while (tmp != nil) + { + tmp = [self outputBlock: tmp to: buf inPara: NO]; + } [self decIndent]; [buf appendString: indent]; [buf appendString: @"
  • \n"]; @@ -1325,6 +1346,8 @@ NSLog(@"Element '%@' not implemented", name); // FIXME [self incIndent]; while (children != nil) { + GSXMLNode *tmp; + [buf appendString: indent]; [buf appendString: @"
    "]; [self outputText: [children children] to: buf]; @@ -1333,7 +1356,11 @@ NSLog(@"Element '%@' not implemented", name); // FIXME [buf appendString: indent]; [buf appendString: @"
    \n"]; [self incIndent]; - [self outputBlock: [children children] to: buf inPara: NO]; + tmp = [children children]; + while (tmp != nil) + { + tmp = [self outputBlock: tmp to: buf inPara: NO]; + } [self decIndent]; [buf appendString: indent]; [buf appendString: @"
    \n"]; @@ -1350,6 +1377,8 @@ NSLog(@"Element '%@' not implemented", name); // FIXME [self incIndent]; while (children != nil) { + GSXMLNode *tmp; + [buf appendString: indent]; [buf appendString: @"
    "]; [self outputText: [children children] to: buf]; @@ -1358,7 +1387,11 @@ NSLog(@"Element '%@' not implemented", name); // FIXME [buf appendString: indent]; [buf appendString: @"
    \n"]; [self incIndent]; - [self outputBlock: [children children] to: buf inPara: NO]; + tmp = [children children]; + while (tmp != nil) + { + tmp = [self outputBlock: tmp to: buf inPara: NO]; + } [self decIndent]; [buf appendString: indent]; [buf appendString: @"
    \n"];