// Copyright 2018 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#include "fpdfsdk/cpdfsdk_helpers.h"

#include "constants/stream_dict_common.h"
#include "core/fpdfapi/cpdf_modulemgr.h"
#include "core/fpdfapi/page/cpdf_page.h"
#include "core/fpdfapi/parser/cpdf_array.h"
#include "core/fpdfapi/parser/cpdf_document.h"
#include "core/fpdfapi/parser/fpdf_parser_decode.h"
#include "core/fpdfdoc/cpdf_annot.h"
#include "core/fpdfdoc/cpdf_interform.h"
#include "core/fpdfdoc/cpdf_metadata.h"
#include "public/fpdf_ext.h"

#ifdef PDF_ENABLE_XFA
#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
#endif

namespace {

constexpr char kQuadPoints[] = "QuadPoints";

bool RaiseUnSupportError(int nError) {
  CFSDK_UnsupportInfo_Adapter* pAdapter =
      CPDF_ModuleMgr::Get()->GetUnsupportInfoAdapter();
  if (!pAdapter)
    return false;

  UNSUPPORT_INFO* info = static_cast<UNSUPPORT_INFO*>(pAdapter->GetUnspInfo());
  if (info && info->FSDK_UnSupport_Handler)
    info->FSDK_UnSupport_Handler(info, nError);
  return true;
}

#ifdef PDF_ENABLE_XFA
class FPDF_FileHandlerContext : public IFX_SeekableStream {
 public:
  template <typename T, typename... Args>
  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);

  ~FPDF_FileHandlerContext() override;

  // IFX_SeekableStream:
  FX_FILESIZE GetSize() override;
  bool IsEOF() override;
  FX_FILESIZE GetPosition() override;
  bool ReadBlock(void* buffer, FX_FILESIZE offset, size_t size) override;
  size_t ReadBlock(void* buffer, size_t size) override;
  bool WriteBlock(const void* buffer, FX_FILESIZE offset, size_t size) override;
  bool Flush() override;

  void SetPosition(FX_FILESIZE pos) { m_nCurPos = pos; }

 protected:
  explicit FPDF_FileHandlerContext(FPDF_FILEHANDLER* pFS);

  FPDF_FILEHANDLER* m_pFS;
  FX_FILESIZE m_nCurPos;
};

FPDF_FileHandlerContext::FPDF_FileHandlerContext(FPDF_FILEHANDLER* pFS) {
  m_pFS = pFS;
  m_nCurPos = 0;
}

FPDF_FileHandlerContext::~FPDF_FileHandlerContext() {
  if (m_pFS && m_pFS->Release)
    m_pFS->Release(m_pFS->clientData);
}

FX_FILESIZE FPDF_FileHandlerContext::GetSize() {
  if (m_pFS && m_pFS->GetSize)
    return (FX_FILESIZE)m_pFS->GetSize(m_pFS->clientData);
  return 0;
}

bool FPDF_FileHandlerContext::IsEOF() {
  return m_nCurPos >= GetSize();
}

FX_FILESIZE FPDF_FileHandlerContext::GetPosition() {
  return m_nCurPos;
}

bool FPDF_FileHandlerContext::ReadBlock(void* buffer,
                                        FX_FILESIZE offset,
                                        size_t size) {
  if (!buffer || !size || !m_pFS->ReadBlock)
    return false;

  if (m_pFS->ReadBlock(m_pFS->clientData, (FPDF_DWORD)offset, buffer,
                       (FPDF_DWORD)size) == 0) {
    m_nCurPos = offset + size;
    return true;
  }
  return false;
}

size_t FPDF_FileHandlerContext::ReadBlock(void* buffer, size_t size) {
  if (!buffer || !size || !m_pFS->ReadBlock)
    return 0;

  FX_FILESIZE nSize = GetSize();
  if (m_nCurPos >= nSize)
    return 0;
  FX_FILESIZE dwAvail = nSize - m_nCurPos;
  if (dwAvail < (FX_FILESIZE)size)
    size = static_cast<size_t>(dwAvail);
  if (m_pFS->ReadBlock(m_pFS->clientData, (FPDF_DWORD)m_nCurPos, buffer,
                       (FPDF_DWORD)size) == 0) {
    m_nCurPos += size;
    return size;
  }

  return 0;
}

bool FPDF_FileHandlerContext::WriteBlock(const void* buffer,
                                         FX_FILESIZE offset,
                                         size_t size) {
  if (!m_pFS || !m_pFS->WriteBlock)
    return false;

  if (m_pFS->WriteBlock(m_pFS->clientData, (FPDF_DWORD)offset, buffer,
                        (FPDF_DWORD)size) == 0) {
    m_nCurPos = offset + size;
    return true;
  }
  return false;
}

bool FPDF_FileHandlerContext::Flush() {
  if (!m_pFS || !m_pFS->Flush)
    return true;

  return m_pFS->Flush(m_pFS->clientData) == 0;
}
#endif  // PDF_ENABLE_XFA

}  // namespace

UnderlyingPageType* UnderlyingFromFPDFPage(FPDF_PAGE page) {
  return static_cast<UnderlyingPageType*>(page);
}

CPDF_Document* CPDFDocumentFromFPDFDocument(FPDF_DOCUMENT doc) {
  return reinterpret_cast<CPDF_Document*>(doc);
}

FPDF_DOCUMENT FPDFDocumentFromCPDFDocument(CPDF_Document* doc) {
#ifdef PDF_ENABLE_XFA
  if (doc && !doc->GetExtension())
    doc->SetExtension(new CPDFXFA_Context(pdfium::WrapUnique(doc)));
#endif  // PDF_ENABLE_XFA
  return reinterpret_cast<FPDF_DOCUMENT>(doc);
}

CPDF_Page* CPDFPageFromFPDFPage(FPDF_PAGE page) {
#ifdef PDF_ENABLE_XFA
  return page ? UnderlyingFromFPDFPage(page)->GetPDFPage() : nullptr;
#else   // PDF_ENABLE_XFA
  return UnderlyingFromFPDFPage(page);
#endif  // PDF_ENABLE_XFA
}

CPDF_PageObject* CPDFPageObjectFromFPDFPageObject(FPDF_PAGEOBJECT page_object) {
  return static_cast<CPDF_PageObject*>(page_object);
}

ByteString CFXByteStringFromFPDFWideString(FPDF_WIDESTRING wide_string) {
  return WideString::FromUTF16LE(wide_string,
                                 WideString::WStringLength(wide_string))
      .UTF8Encode();
}

CFX_DIBitmap* CFXBitmapFromFPDFBitmap(FPDF_BITMAP bitmap) {
  return static_cast<CFX_DIBitmap*>(bitmap);
}

void CheckUnSupportAnnot(CPDF_Document* pDoc, const CPDF_Annot* pPDFAnnot) {
  CPDF_Annot::Subtype nAnnotSubtype = pPDFAnnot->GetSubtype();
  if (nAnnotSubtype == CPDF_Annot::Subtype::THREED) {
    RaiseUnSupportError(FPDF_UNSP_ANNOT_3DANNOT);
  } else if (nAnnotSubtype == CPDF_Annot::Subtype::SCREEN) {
    const CPDF_Dictionary* pAnnotDict = pPDFAnnot->GetAnnotDict();
    ByteString cbString;
    if (pAnnotDict->KeyExist("IT"))
      cbString = pAnnotDict->GetStringFor("IT");
    if (cbString.Compare("Img") != 0)
      RaiseUnSupportError(FPDF_UNSP_ANNOT_SCREEN_MEDIA);
  } else if (nAnnotSubtype == CPDF_Annot::Subtype::MOVIE) {
    RaiseUnSupportError(FPDF_UNSP_ANNOT_MOVIE);
  } else if (nAnnotSubtype == CPDF_Annot::Subtype::SOUND) {
    RaiseUnSupportError(FPDF_UNSP_ANNOT_SOUND);
  } else if (nAnnotSubtype == CPDF_Annot::Subtype::RICHMEDIA) {
    RaiseUnSupportError(FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA);
  } else if (nAnnotSubtype == CPDF_Annot::Subtype::FILEATTACHMENT) {
    RaiseUnSupportError(FPDF_UNSP_ANNOT_ATTACHMENT);
  } else if (nAnnotSubtype == CPDF_Annot::Subtype::WIDGET) {
    const CPDF_Dictionary* pAnnotDict = pPDFAnnot->GetAnnotDict();
    ByteString cbString;
    if (pAnnotDict->KeyExist("FT"))
      cbString = pAnnotDict->GetStringFor("FT");
    if (cbString.Compare("Sig") == 0)
      RaiseUnSupportError(FPDF_UNSP_ANNOT_SIG);
  }
}

void CheckUnSupportError(CPDF_Document* pDoc, uint32_t err_code) {
  // Security
  if (err_code == FPDF_ERR_SECURITY) {
    RaiseUnSupportError(FPDF_UNSP_DOC_SECURITY);
    return;
  }
  if (!pDoc)
    return;

  // Portfolios and Packages
  const CPDF_Dictionary* pRootDict = pDoc->GetRoot();
  if (pRootDict) {
    ByteString cbString;
    if (pRootDict->KeyExist("Collection")) {
      RaiseUnSupportError(FPDF_UNSP_DOC_PORTABLECOLLECTION);
      return;
    }
    if (pRootDict->KeyExist("Names")) {
      CPDF_Dictionary* pNameDict = pRootDict->GetDictFor("Names");
      if (pNameDict && pNameDict->KeyExist("EmbeddedFiles")) {
        RaiseUnSupportError(FPDF_UNSP_DOC_ATTACHMENT);
        return;
      }
      if (pNameDict && pNameDict->KeyExist("JavaScript")) {
        CPDF_Dictionary* pJSDict = pNameDict->GetDictFor("JavaScript");
        CPDF_Array* pArray = pJSDict ? pJSDict->GetArrayFor("Names") : nullptr;
        if (pArray) {
          for (size_t i = 0; i < pArray->GetCount(); i++) {
            ByteString cbStr = pArray->GetStringAt(i);
            if (cbStr.Compare("com.adobe.acrobat.SharedReview.Register") == 0) {
              RaiseUnSupportError(FPDF_UNSP_DOC_SHAREDREVIEW);
              return;
            }
          }
        }
      }
    }
  }

  // SharedForm
  const CPDF_Dictionary* pRoot = pDoc->GetRoot();
  if (pRoot) {
    CPDF_Stream* pStream = pRoot->GetStreamFor("Metadata");
    if (pStream) {
      CPDF_Metadata metaData(pStream);
      for (const auto& err : metaData.CheckForSharedForm())
        RaiseUnSupportError(static_cast<int>(err));
    }
  }

#ifndef PDF_ENABLE_XFA
  // XFA Forms
  CPDF_InterForm interform(pDoc);
  if (interform.HasXFAForm())
    RaiseUnSupportError(FPDF_UNSP_DOC_XFAFORM);
#endif  // PDF_ENABLE_XFA
}

#ifndef _WIN32
int g_LastError;
void SetLastError(int err) {
  g_LastError = err;
}

int GetLastError() {
  return g_LastError;
}
#endif  // _WIN32

void ProcessParseError(CPDF_Parser::Error err) {
  uint32_t err_code = FPDF_ERR_SUCCESS;
  // Translate FPDFAPI error code to FPDFVIEW error code
  switch (err) {
    case CPDF_Parser::SUCCESS:
      err_code = FPDF_ERR_SUCCESS;
      break;
    case CPDF_Parser::FILE_ERROR:
      err_code = FPDF_ERR_FILE;
      break;
    case CPDF_Parser::FORMAT_ERROR:
      err_code = FPDF_ERR_FORMAT;
      break;
    case CPDF_Parser::PASSWORD_ERROR:
      err_code = FPDF_ERR_PASSWORD;
      break;
    case CPDF_Parser::HANDLER_ERROR:
      err_code = FPDF_ERR_SECURITY;
      break;
  }
  SetLastError(err_code);
}

// 0 bit: FPDF_POLICY_MACHINETIME_ACCESS
static uint32_t foxit_sandbox_policy = 0xFFFFFFFF;

void FSDK_SetSandBoxPolicy(FPDF_DWORD policy, FPDF_BOOL enable) {
  switch (policy) {
    case FPDF_POLICY_MACHINETIME_ACCESS: {
      if (enable)
        foxit_sandbox_policy |= 0x01;
      else
        foxit_sandbox_policy &= 0xFFFFFFFE;
    } break;
    default:
      break;
  }
}

FPDF_BOOL FSDK_IsSandBoxPolicyEnabled(FPDF_DWORD policy) {
  switch (policy) {
    case FPDF_POLICY_MACHINETIME_ACCESS:
      return !!(foxit_sandbox_policy & 0x01);
    default:
      return false;
  }
}

unsigned long DecodeStreamMaybeCopyAndReturnLength(const CPDF_Stream* stream,
                                                   void* buffer,
                                                   unsigned long buflen) {
  ASSERT(stream);
  uint8_t* data = stream->GetRawData();
  uint32_t len = stream->GetRawSize();
  CPDF_Dictionary* dict = stream->GetDict();
  CPDF_Object* decoder = dict ? dict->GetDirectObjectFor("Filter") : nullptr;
  if (decoder && (decoder->IsArray() || decoder->IsName())) {
    // Decode the stream if one or more stream filters are specified.
    uint8_t* decoded_data = nullptr;
    uint32_t decoded_len = 0;
    ByteString dummy_last_decoder;
    CPDF_Dictionary* dummy_last_param;
    if (PDF_DataDecode(data, len, dict,
                       dict->GetIntegerFor(pdfium::stream::kDL), false,
                       &decoded_data, &decoded_len, &dummy_last_decoder,
                       &dummy_last_param)) {
      if (buffer && buflen >= decoded_len)
        memcpy(buffer, decoded_data, decoded_len);

      // Free the buffer for the decoded data if it was allocated by
      // PDF_DataDecode(). Note that for images with a single image-specific
      // filter, |decoded_data| is directly assigned to be |data|, so
      // |decoded_data| does not need to be freed.
      if (decoded_data != data)
        FX_Free(decoded_data);

      return decoded_len;
    }
  }
  // Copy the raw data and return its length if there is no valid filter
  // specified or if decoding failed.
  if (buffer && buflen >= len)
    memcpy(buffer, data, len);

  return len;
}

unsigned long Utf16EncodeMaybeCopyAndReturnLength(const WideString& text,
                                                  void* buffer,
                                                  unsigned long buflen) {
  ByteString encoded_text = text.UTF16LE_Encode();
  unsigned long len = encoded_text.GetLength();
  if (buffer && len <= buflen)
    memcpy(buffer, encoded_text.c_str(), len);
  return len;
}

void FSRECTFFromCFXFloatRect(const CFX_FloatRect& rect, FS_RECTF* out_rect) {
  out_rect->left = rect.left;
  out_rect->top = rect.top;
  out_rect->right = rect.right;
  out_rect->bottom = rect.bottom;
}

CFX_FloatRect CFXFloatRectFromFSRECTF(const FS_RECTF& rect) {
  return CFX_FloatRect(rect.left, rect.bottom, rect.right, rect.top);
}

CPDF_Array* GetQuadPointsArrayFromDictionary(const CPDF_Dictionary* dict) {
  return dict ? dict->GetArrayFor("QuadPoints") : nullptr;
}

CPDF_Array* AddQuadPointsArrayToDictionary(CPDF_Dictionary* dict) {
  if (!dict)
    return nullptr;
  return dict->SetNewFor<CPDF_Array>(kQuadPoints);
}

bool IsValidQuadPointsIndex(const CPDF_Array* array, size_t index) {
  return array && index < array->GetCount() / 8;
}

bool GetQuadPointsAtIndex(const CPDF_Array* array,
                          size_t quad_index,
                          FS_QUADPOINTSF* quad_points) {
  ASSERT(quad_points);
  ASSERT(array);

  if (!IsValidQuadPointsIndex(array, quad_index))
    return false;

  quad_index *= 8;
  quad_points->x1 = array->GetNumberAt(quad_index);
  quad_points->y1 = array->GetNumberAt(quad_index + 1);
  quad_points->x2 = array->GetNumberAt(quad_index + 2);
  quad_points->y2 = array->GetNumberAt(quad_index + 3);
  quad_points->x3 = array->GetNumberAt(quad_index + 4);
  quad_points->y3 = array->GetNumberAt(quad_index + 5);
  quad_points->x4 = array->GetNumberAt(quad_index + 6);
  quad_points->y4 = array->GetNumberAt(quad_index + 7);
  return true;
}

bool GetQuadPointsFromDictionary(CPDF_Dictionary* dict,
                                 size_t quad_index,
                                 FS_QUADPOINTSF* quad_points) {
  ASSERT(quad_points);

  const CPDF_Array* pArray = GetQuadPointsArrayFromDictionary(dict);
  if (!pArray || quad_index >= pArray->GetCount() / 8)
    return false;

  quad_index *= 8;
  quad_points->x1 = pArray->GetNumberAt(quad_index);
  quad_points->y1 = pArray->GetNumberAt(quad_index + 1);
  quad_points->x2 = pArray->GetNumberAt(quad_index + 2);
  quad_points->y2 = pArray->GetNumberAt(quad_index + 3);
  quad_points->x3 = pArray->GetNumberAt(quad_index + 4);
  quad_points->y3 = pArray->GetNumberAt(quad_index + 5);
  quad_points->x4 = pArray->GetNumberAt(quad_index + 6);
  quad_points->y4 = pArray->GetNumberAt(quad_index + 7);
  return true;
}

#ifdef PDF_ENABLE_XFA
RetainPtr<IFX_SeekableStream> MakeSeekableStream(
    FPDF_FILEHANDLER* pFilehandler) {
  return pdfium::MakeRetain<FPDF_FileHandlerContext>(pFilehandler);
}
#endif  // PDF_ENABLE_XFA