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()); } }
// // 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); } } }
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; }