void
GMMIMEParser::ParseMIMEHeader
	(
	std::istream&		input,
	GMIMEHeader*	header,
	const JBoolean	display
	)
{
	JString data;
	JCharacter c	= input.peek();
	if (c == '\n')
		{
//		input.get(c);
		}
//	input >> std::ws;

	// first we need to search for the first empty line. This line is the
	// end of the header.

	JString line;
	while (1)
		{
		JBoolean found;
		line = JReadLine(input, &found);
		if (line.IsEmpty())
			{
			break;
			}
		if (isspace(line.GetFirstCharacter()))
			{
			line.TrimWhitespace();
			if (line.IsEmpty())
				{
				break;
				}
			data.AppendCharacter(' ');
			}
		else if (!data.IsEmpty())
			{
			data.AppendCharacter('\n');
			}
		data += line;
		}
	data.AppendCharacter('\n');

	// we now need to search through the header for parameter:value pairs
	// using the gmime_header_regex defined above.

	JArray<JIndexRange> ranges;
	gmime_header_regex.MatchAll(data, &ranges);

	JSize count = ranges.GetElementCount();
	for (JSize i = 1; i <= count; i++)
		{
		JIndexRange range = ranges.GetElement(i);
		JString parmValPair = data.GetSubstring(range);
		JString parm;
		JString val;
		if (parmValPair.BeginsWith("MIME") ||
			parmValPair.BeginsWith("Mime") ||
			parmValPair.BeginsWith("Content"))
			{
			CleanParmValPair(parmValPair, &parm, &val);
			parm.ToLower();
			if (parm == "mime-Version")
				{
				val.TrimWhitespace();
				header->SetVersion(val);
				}
			else if (parm == "content-type")
				{
				ParseContentType(val, header);
				}
			else if (parm == "content-transfer-encoding")
				{
				val.TrimWhitespace();
				val.ToLower();
				header->SetEncoding(val);
				}
			else if (parm == "content-disposition")
				{
				ParseContentDisposition(val, header);
				}
			}
		}

	// this is a nested message, so some of the headers need to be displayed
	if (display)
		{
		JString text = "---------\n";
		JIndex findex	= 1;
		if (data.BeginsWith("From: ") || data.LocateSubstring("\nFrom: ", &findex))
			{
			if (findex > 1)
				{
				findex ++;
				}
			JIndex eindex	= findex;
			if (data.LocateNextSubstring("\n", &eindex) && (eindex > findex + 1))
				{
				text += data.GetSubstring(findex, eindex - 1);
				text += "\n";
				}
			}
		findex	= 1;
		if (data.BeginsWith("Date: ") || data.LocateSubstring("\nDate: ", &findex))
			{
			if (findex > 1)
				{
				findex ++;
				}
			JIndex eindex	= findex;
			if (data.LocateNextSubstring("\n", &eindex) && (eindex > findex + 1))
				{
				text += data.GetSubstring(findex, eindex - 1);
				text += "\n";
				}
			}
		findex	= 1;
		const JCharacter* kSubjectStr	= "Subject: ";
		if (data.BeginsWith("Subject: ") || data.LocateSubstring("\nSubject: ", &findex))
			{
			if (findex > 1)
				{
				findex ++;
				}
			JIndex eindex	= findex;
			if (data.LocateNextSubstring("\n", &eindex) && (eindex > findex + 1))
				{
				text += data.GetSubstring(findex, eindex - 1);
				text += "\n";
				}
			}
		WriteTextString(&text, GMIMEHeader());
		}
}
Example #2
0
//
// Parse chunk of data
//
void MultipartParser::ParseChunk(CCHAR_P szString, CCHAR_P szStringEnd)
{
	// Last state of FSM, nothing to do
	if (iState == C_BOUNDARY_IN_SUFFIX4 || iState == C_END_MIME) { return; }

	CCHAR_P sStartPos        = szString;
	bool bFoundEndOfBoundary = false;
	bool bFoundEndOfKey      = false;
	bool bFoundEndOfSpace    = false;

	if (iState == C_BOUNDARY_IN)         { goto BOUNDARY_IN;         }
	if (iState == C_BOUNDARY_IN_SUFFIX1) { goto BOUNDARY_IN_SUFFIX1; }
	if (iState == C_BOUNDARY_IN_SUFFIX2) { goto BOUNDARY_IN_SUFFIX2; }
	if (iState == C_BOUNDARY_IN_SUFFIX3) { goto BOUNDARY_IN_SUFFIX3; }
	if (iState == C_BOUNDARY_IN_SUFFIX4) { goto BOUNDARY_IN_SUFFIX4; }
	if (iState == C_BOUNDARY_IN_SUFFIX5) { goto BOUNDARY_IN_SUFFIX5; }
	if (iState == C_BOUNDARY_IN_SUFFIX6) { goto BOUNDARY_IN_SUFFIX6; }

	if (iState == C_HEADER)              { goto HEADER;     }

	if (iState == C_KEY_NAME)            { goto KEY_NAME;   }
	if (iState == C_KEY_SPACE)           { goto KEY_SPACE;  }
	if (iState == C_KEY_VALUE)           { goto KEY_VALUE;  }

	if (iState == C_HEADER_CR)           { goto HEADER_CR;  }
	if (iState == C_HEADER_LF)           { goto HEADER_LF;  }
	if (iState == C_HEADER_CR2)          { goto HEADER_CR2; }
	if (iState == C_HEADER_LF2)          { goto HEADER_LF2; }

	if (iState == C_BODY_FILE)           { goto BODY_FILE;  }
	if (iState == C_BODY_VALUE)          { goto BODY_VALUE; }
//oLogger.Error("PARSE-CHUNK %u", UINT_32(szStringEnd - szString));
	for(;;)
	{
		//
		// \r - \n - dash - dash - BOUNADRY [dash - dash] - \r - \n C_END_MIME
		// +------------------------------+  |      |       |    |
		// |                                 |      |       |    |
		// |                                 |      |       |    BOUNDARY_IN_SUFFIX4, C_END_MIME
		// |                                 |      |       |
		// |                                 |      |       BOUNDARY_IN_SUFFIX3
		// |                                 |      |
		// |                                 |      BOUNDARY_IN_SUFFIX2
		// |                                 |
		// |                                 BOUNDARY_IN_SUFFIX1
		// |
		// C_BOUNDARY_IN
		//
		// OR
		//
		// \r - \n - dash - dash - BOUNADRY - \r - \n
		// +------------------------------+   |    |
		// |                                  |    |
		// |                                  |    BOUNDARY_IN_SUFFIX6
		// |                                  |
		// |                                  BOUNDARY_IN_SUFFIX5
		// |
		// C_BOUNDARY_IN
		//
		if (iState == C_BOUNDARY_IN)
		{
BOUNDARY_IN:
			sStartPos = szString;

			bFoundEndOfBoundary = false;

			for(;;)
			{
				// Boundary not found, error
				if (*szBoundaryPos != *szString)
				{
					iState        = iPrevState;
					szBoundaryPos = sBoundary.data();
//oLogger.Error("NOT-A-BOUNDARY");
					break;
				}

				++szBoundaryPos; ++szString;

				// Found end of boundary, okay
				if (*szBoundaryPos == '\0') { bFoundEndOfBoundary = true; break; }

				// Found part of boundary
				if (szString == szStringEnd) { break; }
			}
			if (!bFoundEndOfBoundary) { sBuffer.append(sStartPos, szString - sStartPos); }

			// Store part of boundary in temp. buffer
			if (szString == szStringEnd) { return; }

			// Check next state
			if (bFoundEndOfBoundary)
			{
				// Check BOUNDARY_IN_SUFFIX1
				if (*szString == '-')
				{
					iState = C_BOUNDARY_IN_SUFFIX1;
					++szString;
BOUNDARY_IN_SUFFIX1:
					// Store part of boundary in temp. buffer
					if (szString == szStringEnd)
					{
						sBuffer.append(sStartPos, szString - sStartPos);
						return;
					}

					// Return to prev. state
					if (*szString != '-') { iState = iPrevState; }
					// Check BOUNDARY_IN_SUFFIX2
					else
					{
						iState = C_BOUNDARY_IN_SUFFIX2;
						++szString;
BOUNDARY_IN_SUFFIX2:
						// Store part of boundary in temp. buffer
						if (szString == szStringEnd)
						{
							sBuffer.append(sStartPos, szString - sStartPos);
							return;
						}

						// Return to prev. state
						if (*szString != '\r') { iState = iPrevState; }
						// Check BOUNDARY_IN_SUFFIX3
						else
						{
							iState = C_BOUNDARY_IN_SUFFIX3;
							++szString;
BOUNDARY_IN_SUFFIX3:
							// Store part of boundary in temp. buffer
							if (szString == szStringEnd)
							{
								sBuffer.append(sStartPos, szString - sStartPos);
								return;
							}

							// Return to prev. state
							if (*szString != '\n') { iState = iPrevState; }
							// Check BOUNDARY_IN_SUFFIX4
							else
							{
								iState = C_BOUNDARY_IN_SUFFIX4;
								++szString;
BOUNDARY_IN_SUFFIX4:
								// Commit section
								CommitSection();

								if (szString == szStringEnd)
								{
									iState = C_END_MIME;
//oLogger.Error("END-OF-MIME AND END-OF-BUFFER");
									return;
								}

//oLogger.Error("END-OF-MIME AND NOT END-OF-BUFFER");
								return;
							}
						}
					}
				}
				// Check BOUNDARY_IN_SUFFIX5
				if (*szString == '\r')
				{
					iState = C_BOUNDARY_IN_SUFFIX5;
					++szString;
BOUNDARY_IN_SUFFIX5:
					// Store part of boundary in temp. buffer
					if (szString == szStringEnd)
					{
						sBuffer.append(sStartPos, szString - sStartPos);
						return;
					}

					// Return to prev. state
					if (*szString != '\n') { iState = iPrevState; }
					// Check BOUNDARY_IN_SUFFIX6
					else
					{
						// End of MIME part of message reached
						iState = C_BOUNDARY_IN_SUFFIX6;
						++szString;
BOUNDARY_IN_SUFFIX6:
//oLogger.Error("END-OF-MIME-PART");

						// Commit section
						CommitSection();
					}
				}
				// Return to prev. state
				else { iState = iPrevState; }
			}
		}

		// C_HEADER
		// Header: [space] HeaderValue; optional="field" \r - \n [ \r - \n ]
		// |        |      |                              |    |    |    |
		// |        |      |                              |    |    |    C_HEADER_LF2
		// |        |      |                              |    |    |
		// |        |      |                              |    |    HEADER_CR2
		// |        |      |                              |    |
		// |        |      |                              |    HEADER_LF
		// |        |      |                              |
		// |        |      |                              HEADER_CR
		// |        |      |
		// |        |      C_KEY_VALUE
		// |        |
		// |        C_KEY_PRE_VALUE
		// |
		// C_KEY_NAME
		//
		// New MIME part
		if (iState == C_BOUNDARY_IN_SUFFIX6 ||
		    iState == C_HEADER ||
		    iState == C_KEY_NAME)
		{
			iState = C_HEADER;
HEADER:
//oLogger.Error("NEW MIME PART");

			if (szString == szStringEnd) { return; }
			sStartPos = szString;

			for (;;)
			{
				// Check \r\n
				if (*szString != '\r')
				{
					iState = C_KEY_NAME;
KEY_NAME:
					if (szString == szStringEnd) { return; }
					sStartPos = szString;

					for(;;)
					{
						// End of key
						if (*szString == ':') { bFoundEndOfKey = true; break; }
						// Invalid header
						if (*szString == '\r') { break; }
						++szString;

						// Found end of buffer
						if (szString == szStringEnd) { return; }
					}
					sBuffer.append(sStartPos, szString - sStartPos);

					if (!bFoundEndOfKey)
					{
						// Clear buffer
						sBuffer.erase();
//oLogger.Error("INVALID-HEADER-KEY");
					}
					else
					{
//oLogger.Error("HEADER-KEY `%s`", sBuffer.data());
						// Compare header name
						if (strncasecmp(C_CONTENT_DISPOSITION, sBuffer.data(), sBuffer.size()) == 0)
						{
//oLogger.Error("CONTENT-DISPOSITION");
							iHeaderKeyType = C_KEY_CONTENT_DISPOSITION;
						}
						else if (strncasecmp(C_CONTENT_TYPE, sBuffer.data(), sBuffer.size()) == 0)
						{
//oLogger.Error("CONTENT-TYPE");
							iHeaderKeyType = C_KEY_CONTENT_TYPE;
						}

						iState = C_KEY_SPACE;
						++szString;
						// Clear buffer
						sBuffer.erase();
KEY_SPACE:
						if (szString == szStringEnd) { break; }
						sStartPos = szString;

						for(;;)
						{
							// Invalid header
							if (*szString == '\r') { break; }
							// End of key
							if (*szString != ' ') { bFoundEndOfSpace = true; break; }
							++szString;

							// Found end of buffer
							if (szString == szStringEnd) { return; }
						}

						if (!bFoundEndOfSpace)
						{
							iState = C_KEY_NAME;
//oLogger.Error("INVALID-HEADER-SPACE");
						}
						else
						{
							iState = C_KEY_VALUE;
KEY_VALUE:
							if (szString == szStringEnd) { break; }
							sStartPos = szString;

							for(;;)
							{
								// Invalid header
								if (*szString == '\r') { break; }
								++szString;

								// Found end of buffer
								if (szString == szStringEnd) { return; }
							}
							sBuffer.append(sStartPos, szString - sStartPos);
//oLogger.Error("HEADER-VALUE `%s`", sBuffer.data());
							// Parse header
							if      (iHeaderKeyType == C_KEY_CONTENT_DISPOSITION) { ParseContentDisposition(); }
							else if (iHeaderKeyType == C_KEY_CONTENT_TYPE)        { oFile.content_type = sBuffer; }

							// Clear buffer
							sBuffer.erase();
						}
					}
				}

				// Check \r\n
				if (*szString == '\r')
				{
					iState = C_HEADER_CR;
					++szString;
HEADER_CR:
					// End of buffer
					if (szString == szStringEnd) { return; }

					// Check error
					if (*szString != '\n') { iState = C_ERROR; return; }
					// Line feed
					else
					{
						iState = C_HEADER_LF;
						++szString;
HEADER_LF:
						// End of buffer
						if (szString == szStringEnd) { return; }

						// Check \r\n, end of headers
						if (*szString == '\r')
						{
							iState = C_HEADER_CR2;
							++szString;
HEADER_CR2:
							// End of buffer
							if (szString == szStringEnd) { return; }

							// Check error
							if (*szString != '\n') { iState = C_ERROR; return; }
							{
								iState = C_HEADER_LF2;
								++szString;
HEADER_LF2:
								// End of buffer
								if (szString == szStringEnd) { return; }

								iState = oFile.full_filename.empty() ? C_BODY_VALUE : C_BODY_FILE;
								oFile.filesize = 0;
//oLogger.Error("END-OF-ALL-HEADERS-%s", oFile.full_filename.empty() ? "STORE-FIELD" : "WRITE-FILE");
								break;
							}
						}
						else
						{
							iState = C_KEY_NAME;
						}
//oLogger.Error("END-OF-HEADER");
					}
				}

				if (szString == szStringEnd) { return; }
			}
		}

		// This is file
		if (iState == C_BODY_FILE)
		{
BODY_FILE:
			// Write chunk
			WriteFile(sBuffer.data(), sBuffer.data() + sBuffer.size());
			sBuffer.erase();

			if (szString == szStringEnd) { break; }

			sStartPos = szString;
			for (;;)
			{
				if (*szString == '\r')
				{
					iPrevState = C_BODY_FILE;
					iState     = C_BOUNDARY_IN;
					szBoundaryPos = sBoundary.data();
//oLogger.Error("FILE-CHECK-BOUNDARY");
					break;
				}

				++szString;
				if (szString == szStringEnd) { break; }
			}

			// Write chunk
			WriteFile(sStartPos, szString);
		}

		// This is value
		if (iState == C_BODY_VALUE)
		{
BODY_VALUE:
			if (szString == szStringEnd) { break; }

			sStartPos = szString;
			for (;;)
			{
				if (*szString == '\r')
				{
					iPrevState = C_BODY_VALUE;
					iState     = C_BOUNDARY_IN;
					szBoundaryPos = sBoundary.data();
//oLogger.Error("VALUE-CHECK-BOUNDARY");
					break;
				}

				++szString;
				if (szString == szStringEnd) { break; }
			}

			// Store chunk
			sBuffer.append(sStartPos, szString - sStartPos);
		}
	}
}
Example #3
0
String URLUtil::GuessFileName(
    /* [in] */ const String& url,
    /* [in] */ const String& contentDisposition,
    /* [in] */ const String& _mimeType)
{
    String filename;
    String extension;
    String mimeType = _mimeType;

    // If we couldn't do anything with the hint, move toward the content disposition
    if (filename.IsNull() && !contentDisposition.IsNull()) {
        filename = ParseContentDisposition(contentDisposition);
        if (!filename.IsNull()) {
            Int32 index = filename.LastIndexOf('/') + 1;
            if (index > 0) {
                filename = filename.Substring(index);
            }
        }
    }

    // If all the other http-related approaches failed, use the plain uri
    if (filename.IsNull()) {
        String decodedUrl;
        Uri::Decode(url, &decodedUrl);
        if (!decodedUrl.IsNull()) {
            Int32 queryIndex = decodedUrl.IndexOf('?');
            // If there is a query string strip it, same as desktop browsers
            if (queryIndex > 0) {
                decodedUrl = decodedUrl.Substring(0, queryIndex);
            }
            if (!decodedUrl.EndWith("/")) {
                Int32 index = decodedUrl.LastIndexOf('/') + 1;
                if (index > 0) {
                    filename = decodedUrl.Substring(index);
                }
            }
        }
    }

    // Finally, if couldn't get filename from URI, get a generic filename
    if (filename.IsNull()) {
        filename = "downloadfile";
    }

    // Split filename between base and extension
    // Add an extension if filename does not have one
    Int32 dotIndex = filename.IndexOf('.');
    if (dotIndex < 0) {
        if (!mimeType.IsNull()) {
            AutoPtr<MimeTypeMap> mimeTypeMap = MimeTypeMap::GetSingleton();
            mimeTypeMap->GetExtensionFromMimeType(mimeType, &extension);
            if (!extension.IsNull()) {
                extension = String(".") + extension;
            }
        }
        if (extension.IsNull()) {
            if (!mimeType.IsNull() && mimeType.StartWithIgnoreCase("text/")) {
                if (mimeType.EqualsIgnoreCase("text/html")) {
                    extension = ".html";
                }
                else {
                    extension = ".txt";
                }
            }
            else {
                extension = ".bin";
            }
        }
    }
    else {
        if (!mimeType.IsNull()) {
            // Compare the last segment of the extension against the mime type.
            // If there's a mismatch, discard the entire extension.
            Int32 lastDotIndex = filename.LastIndexOf('.');
            AutoPtr<MimeTypeMap> mimeTypeMap = MimeTypeMap::GetSingleton();
            String typeFromExt;
            mimeTypeMap->GetMimeTypeFromExtension(
                    filename.Substring(lastDotIndex + 1), &typeFromExt);
            if (!typeFromExt.IsNull() && !typeFromExt.EqualsIgnoreCase(mimeType)) {
                mimeTypeMap->GetExtensionFromMimeType(mimeType, &extension);
                if (!extension.IsNull()) {
                    extension = String(".") + extension;
                }
            }
        }
        if (extension.IsNull()) {
            extension = filename.Substring(dotIndex);
        }
        filename = filename.Substring(0, dotIndex);
    }

    return filename + extension;
}