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

#include <memory.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>

#include <vector>

#include "base/scoped_generic.h"
#include "base/test/fuzzed_data_provider.h"
#include "third_party/minizip/src/mz.h"
#include "third_party/minizip/src/mz_strm_mem.h"
#include "third_party/minizip/src/mz_zip.h"

const char kTestFileName[] = "test.zip";

namespace {

struct MzTraitsBase {
  static void* InvalidValue() { return nullptr; }
};

struct MzStreamMemTraits : public MzTraitsBase {
  static void Free(void* stream) { mz_stream_mem_delete(&stream); }
};
typedef base::ScopedGeneric<void*, MzStreamMemTraits> ScopedMzStreamMem;

struct MzZipTraits : public MzTraitsBase {
  static void Free(void* stream) {
    mz_zip_close(stream);
    mz_zip_delete(&stream);
  }
};
typedef base::ScopedGeneric<void*, MzZipTraits> ScopedMzZip;

}  // namespace

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
  base::FuzzedDataProvider data_provider(data, size);

  mz_zip_file file_info = {};
  file_info.flag = MZ_ZIP_FLAG_UTF8;
  if (data_provider.ConsumeIntegral<uint8_t>() < 0x08) {
    file_info.flag = data_provider.ConsumeIntegral<uint16_t>();
  }
  file_info.compression_method = MZ_COMPRESS_METHOD_DEFLATE;
  if (data_provider.ConsumeIntegral<uint8_t>() < 0x08) {
    file_info.compression_method = MZ_COMPRESS_METHOD_STORE;
  } else if (data_provider.ConsumeIntegral<uint8_t>() < 0x08) {
    file_info.compression_method = data_provider.ConsumeIntegral<uint16_t>();
  }
  if (data_provider.ConsumeIntegral<uint8_t>() < 0x08) {
    file_info.zip64 = data_provider.ConsumeIntegral<uint16_t>();
  }
  file_info.filename = kTestFileName;
  file_info.filename_size = sizeof(kTestFileName);

  int16_t compress_level = MZ_COMPRESS_LEVEL_DEFAULT;
  if (data_provider.ConsumeIntegral<uint8_t>() < 0x08) {
    compress_level =
        static_cast<int16_t>(data_provider.ConsumeIntegral<uint16_t>());
  }

  ScopedMzStreamMem out_stream(mz_stream_mem_create(nullptr));

  ScopedMzZip zip_file(mz_zip_create(nullptr));
  int result = mz_zip_open(zip_file.get(), out_stream.get(),
                           MZ_OPEN_MODE_CREATE | MZ_OPEN_MODE_WRITE);
  if (result != MZ_OK) {
    return 0;
  }

  result = mz_zip_entry_write_open(zip_file.get(), &file_info, compress_level,
                                   0, nullptr);
  if (result != MZ_OK) {
    return 0;
  }

  std::vector<uint8_t> remaining_data = data_provider.ConsumeRemainingBytes();
  result = mz_zip_entry_write(zip_file.get(), remaining_data.data(),
                              remaining_data.size());
  if (result != MZ_OK) {
    return 0;
  }

  result = mz_zip_entry_close(zip_file.get());
  if (result != MZ_OK) {
    return 0;
  }

  result = mz_zip_close(zip_file.get());
  if (result != MZ_OK) {
    return 0;
  }

  return 0;
}
