/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nscore.h" #include "nsString.h" #include "prio.h" #include "nsNetUtil.h" #include "nsISeekableStream.h" #include "nsMsgUtils.h" #include "ImportOutFile.h" #include "ImportCharSet.h" #include "ImportDebug.h" /* #ifdef _MAC #define kMacNoCreator '????' #define kMacTextFile 'TEXT' #else #define kMacNoCreator 0 #define kMacTextFile 0 #endif */ ImportOutFile::ImportOutFile() { m_ownsFileAndBuffer = false; m_pos = 0; m_pBuf = nullptr; m_bufSz = 0; m_pTrans = nullptr; m_pTransOut = nullptr; m_pTransBuf = nullptr; } ImportOutFile::ImportOutFile(nsIFile* pFile, uint8_t* pBuf, uint32_t sz) { m_pTransBuf = nullptr; m_pTransOut = nullptr; m_pTrans = nullptr; m_ownsFileAndBuffer = false; InitOutFile(pFile, pBuf, sz); } ImportOutFile::~ImportOutFile() { if (m_ownsFileAndBuffer) { Flush(); delete[] m_pBuf; } delete m_pTrans; delete m_pTransOut; delete[] m_pTransBuf; } bool ImportOutFile::Set8bitTranslator(nsImportTranslator* pTrans) { if (!Flush()) return false; m_engaged = false; m_pTrans = pTrans; m_supports8to7 = pTrans->Supports8bitEncoding(); return true; } bool ImportOutFile::End8bitTranslation(bool* pEngaged, nsCString& useCharset, nsCString& encoding) { if (!m_pTrans) return false; bool bResult = Flush(); if (m_supports8to7 && m_pTransOut) { if (bResult) bResult = m_pTrans->FinishConvertToFile(m_pTransOut); if (bResult) bResult = Flush(); } if (m_supports8to7) { m_pTrans->GetCharset(useCharset); m_pTrans->GetEncoding(encoding); } else useCharset.Truncate(); *pEngaged = m_engaged; delete m_pTrans; m_pTrans = nullptr; delete m_pTransOut; m_pTransOut = nullptr; delete[] m_pTransBuf; m_pTransBuf = nullptr; return bResult; } bool ImportOutFile::InitOutFile(nsIFile* pFile, uint32_t bufSz) { if (!bufSz) bufSz = 32 * 1024; if (!m_pBuf) m_pBuf = new uint8_t[bufSz]; if (!m_outputStream) { nsresult rv; rv = MsgNewBufferedFileOutputStream( getter_AddRefs(m_outputStream), pFile, PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE, 0644); if (NS_FAILED(rv)) { IMPORT_LOG0("Couldn't create outfile\n"); delete[] m_pBuf; m_pBuf = nullptr; return false; } } m_pFile = pFile; m_ownsFileAndBuffer = true; m_pos = 0; m_bufSz = bufSz; return true; } void ImportOutFile::InitOutFile(nsIFile* pFile, uint8_t* pBuf, uint32_t sz) { m_ownsFileAndBuffer = false; m_pFile = pFile; m_pBuf = pBuf; m_bufSz = sz; m_pos = 0; } bool ImportOutFile::Flush(void) { if (!m_pos) return true; uint32_t transLen; bool duddleyDoWrite = false; // handle translations if appropriate if (m_pTrans) { if (m_engaged && m_supports8to7) { // Markers can get confused by this crap!!! // TLR: FIXME: Need to update the markers based on // the difference between the translated len and untranslated len if (!m_pTrans->ConvertToFile(m_pBuf, m_pos, m_pTransOut, &transLen)) return false; if (!m_pTransOut->Flush()) return false; // now update our buffer... if (transLen < m_pos) { memcpy(m_pBuf, m_pBuf + transLen, m_pos - transLen); } m_pos -= transLen; } else if (m_engaged) { // does not actually support translation! duddleyDoWrite = true; } else { // should we engage? uint8_t* pChar = m_pBuf; uint32_t len = m_pos; while (len) { if (!ImportCharSet::IsUSAscii(*pChar)) break; pChar++; len--; } if (len) { m_engaged = true; if (m_supports8to7) { // allocate our translation output buffer and file... m_pTransBuf = new uint8_t[m_bufSz]; m_pTransOut = new ImportOutFile(m_pFile, m_pTransBuf, m_bufSz); return Flush(); } else duddleyDoWrite = true; } else { duddleyDoWrite = true; } } } else duddleyDoWrite = true; if (duddleyDoWrite) { uint32_t written = 0; nsresult rv = m_outputStream->Write((const char*)m_pBuf, (int32_t)m_pos, &written); if (NS_FAILED(rv) || ((uint32_t)written != m_pos)) return false; m_pos = 0; } return true; } bool ImportOutFile::WriteU8NullTerm(const uint8_t* pSrc, bool includeNull) { while (*pSrc) { if (m_pos >= m_bufSz) { if (!Flush()) return false; } *(m_pBuf + m_pos) = *pSrc; m_pos++; pSrc++; } if (includeNull) { if (m_pos >= m_bufSz) { if (!Flush()) return false; } *(m_pBuf + m_pos) = 0; m_pos++; } return true; } bool ImportOutFile::SetMarker(int markerID) { if (!Flush()) { return false; } if (markerID < kMaxMarkers) { int64_t pos = 0; if (m_outputStream) { // do we need to flush for the seek to give us the right pos? m_outputStream->Flush(); nsresult rv; nsCOMPtr seekStream = do_QueryInterface(m_outputStream, &rv); NS_ENSURE_SUCCESS(rv, false); rv = seekStream->Tell(&pos); if (NS_FAILED(rv)) { IMPORT_LOG0("*** Error, Tell failed on output stream\n"); return false; } } m_markers[markerID] = (uint32_t)pos + m_pos; } return true; } void ImportOutFile::ClearMarker(int markerID) { if (markerID < kMaxMarkers) m_markers[markerID] = 0; } bool ImportOutFile::WriteStrAtMarker(int markerID, const char* pStr) { if (markerID >= kMaxMarkers) return false; if (!Flush()) return false; int64_t pos; m_outputStream->Flush(); nsresult rv; nsCOMPtr seekStream = do_QueryInterface(m_outputStream, &rv); NS_ENSURE_SUCCESS(rv, false); rv = seekStream->Tell(&pos); if (NS_FAILED(rv)) return false; rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, (int32_t)m_markers[markerID]); if (NS_FAILED(rv)) return false; uint32_t written; rv = m_outputStream->Write(pStr, strlen(pStr), &written); if (NS_FAILED(rv)) return false; rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, pos); if (NS_FAILED(rv)) return false; return true; }