重构请求部分

This commit is contained in:
xx
2024-06-20 01:38:58 +08:00
parent a26af8da5a
commit d34b2f3098
9 changed files with 12428 additions and 71 deletions

View File

@@ -67,8 +67,8 @@ bool fastweb::interceptor_manager::callback(network::http::reqpack* reqpack, con
sol::error err = script;
throw ylib::exception(err.what());
}
module::request m_request(reqpack->request());
module::response m_response(reqpack->response());
module::request m_request(reqpack->request().get());
module::response m_response(reqpack->response().get());
(*lua->state)["fw_response"] = m_response;
(*lua->state)["fw_request"] = m_request;

View File

@@ -36,8 +36,8 @@ sol::table module::request::body_param(sol::this_state s)
{
sol::state_view lua(s);
sol::table result_table = lua.create_table();
auto map = m_request->parser()->body_param();
for_iter(iter,map)
auto &map = m_request->body_param();
for_iter(iter,(*map.get()))
result_table[iter->first] = iter->second;
return result_table;
}
@@ -45,44 +45,62 @@ sol::table module::request::url_param(sol::this_state s)
{
sol::state_view lua(s);
sol::table result_table = lua.create_table();
auto map = m_request->parser()->url_param();
for_iter(iter, map)
auto& map = m_request->url_param();
for_iter(iter, (*map.get()))
result_table[iter->first] = iter->second;
return result_table;
}
std::string module::request::body()
{
return m_request->parser()->text();
return m_request->body();
}
void* module::request::website()
sol::table module::request::multipart(sol::this_state s)
{
return m_request->website();
}
sol::table module::request::files(sol::this_state s)
{
auto names = m_request->parser()->form()->names();
auto multipart = m_request->multipart();
sol::state_view lua(s);
sol::table result_table = lua.create_table();
int count = 1;
for (size_t i = 0; i < names.size(); i++)
for (size_t i = 0; i < multipart->size(); i++)
{
sol::table file = lua.create_table();
network::http::form_info fi;
if (m_request->parser()->form()->get(names[i], fi))
{
file["type"] = fi.content_type;
file["size"] = fi.data.size();
file["filename"] = fi.filename;
result_table[count] = file;
count++;
sol::table param = lua.create_table();
for_iter(iter, multipart->at(i).param)
{
param[iter->first] = iter->second;// multipart->at(i).header;
file["param"] = param;
}
}
file["id"] = i+1;
file["size"] = multipart->at(i).length;
result_table[count] = file;
count++;
}
return result_table;
}
bool module::request::write_file(const std::string& name, const std::string& filepath)
std::string module::request::multipart_content(int id)
{
return m_request->parser()->form()->write_file(name,filepath);
multipart_content_check(id);
auto mult_info = m_request->multipart()->at(id - 1);
return std::string(m_request->body().data() + mult_info.offset, mult_info.length);
}
bool module::request::multipart_content_save(int id, const std::string& filepath)
{
ylib::file_io file;
if (file.open(filepath) == false)
{
throw ylib::exception("open file failed," + file.last_error());
}
multipart_content_check(id);
auto mult_info = m_request->multipart()->at(id - 1);
if (file.write(m_request->body().data() + mult_info.offset, mult_info.length) == false)
{
throw ylib::exception("write file failed,"+file.last_error());
return false;
}
file.close();
return true;
}
//void module::request::set(const std::string& name, const sol::object& obj, sol::this_state ts)
//{
@@ -115,6 +133,10 @@ std::string module::request::get(const std::string& name)
{
return m_request->reqpack()->extra()[name].to<std::string>();
}
network::http::website* module::request::website()
{
return m_request->website();
}
void module::request::regist(sol::state* lua)
{
// 绑定 Request 类到 Lua
@@ -130,26 +152,33 @@ void module::request::regist(sol::state* lua)
"body_param", &module::request::body_param,
"url_param", &module::request::url_param,
"body", &module::request::body,
"files", &module::request::files,
"write_file", &module::request::write_file,
"multipart", &module::request::multipart,
"multipart_content", &module::request::multipart_content,
"multipart_content_save", &module::request::multipart_content_save,
"get", &module::request::get,
"set", &module::request::set
);
(*lua)["GET"] = (int)network::http::GET;
(*lua)["POST"] = (int)network::http::POST;
(*lua)["DEL"] = (int)network::http::DEL;
(*lua)["HEAD"] = (int)network::http::HEAD;
(*lua)["PUT"] = (int)network::http::PUT;
}
void module::request::multipart_content_check(int id)
{
if (m_request->multipart()->size() < id)
{
throw ylib::exception("The maximum ID is " + std::to_string(m_request->multipart()->size()) + ", and the incoming ID is " + std::to_string(id));
}
auto mult_info = m_request->multipart()->at(id - 1);
if (mult_info.offset + mult_info.length > m_request->body().length())
{
throw ylib::exception("Fastweb internal exception, offset: " + std::to_string(mult_info.offset) + " length: " + std::to_string(mult_info.length) + " body: " + std::to_string(m_request->body().length()));
}
}
std::string module::request::header(const std::string& name)
{
std::string value;
m_request->header(name, value);
return value;
}
ylib::network::http::method module::request::method()
std::string module::request::method()
{
return m_request->method();
}
@@ -176,15 +205,15 @@ sol::object module::request::param(const std::string& name, bool throw_,sol::thi
std::string module::request::remote_ipaddress()
{
return m_request->remote_ipaddress(false);
return m_request->remote().address;
}
ushort module::request::remote_port()
{
return m_request->remote_port();
return m_request->remote().port;
}
bool module::request::request_param(const std::string& name, std::string& value)
{
if (m_request->parser()->url_param(name, value) == false)
return m_request->parser()->body_param(name, value);
if (m_request->get_url_param(name, value) == false)
return m_request->get_body_param(name, value);
return true;
}

View File

@@ -22,7 +22,7 @@ namespace module
/// 取请求类型
/// </summary>
/// <returns></returns>
network::http::method method();
std::string method();
/// <summary>
/// 取请求路径
/// </summary>
@@ -75,29 +75,46 @@ namespace module
/// <returns></returns>
std::string body();
/// <summary>
/// 取网站指针
/// 取表单列表
/// </summary>
/// <returns></returns>
void* website();
sol::table multipart(sol::this_state s);
/// <summary>
/// 取文件列
/// 取表单内容文本
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
sol::table files(sol::this_state s);
std::string multipart_content(int id);
/// <summary>
/// 保存文件
/// </summary>
/// <param name="name"></param>
/// <param name="id"></param>
/// <param name="filepath"></param>
/// <returns></returns>
bool write_file(const std::string& name,const std::string& filepath);
bool multipart_content_save(int id,const std::string& filepath);
/// <summary>
/// 置临时数据
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
void set(const std::string& name,const std::string& value);
/// <summary>
/// 取临时数据
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
std::string get(const std::string& name);
/// <summary>
/// 网站指针
/// </summary>
/// <returns></returns>
network::http::website* website();
static void regist(sol::state* lua);
private:
void multipart_content_check(int id);
bool request_param(const std::string& name, std::string& value);
private:
network::http::request* m_request = nullptr;

View File

@@ -31,7 +31,7 @@ void module::session::init(module::request& request, const std::string& id)
{
throw ylib::exception("the `request` parameter of the init function is nil");
}
m_session->init((network::http::website*)request.website(),id);
m_session->init(request.website(), id);
}
std::string module::session::id()

View File

@@ -1,4 +1,4 @@
/*Software License
/*Software License
Copyright(C) 2024[liuyingjie]
License Terms
@@ -24,7 +24,7 @@ If you have any questions, please contact us: 1585346868@qq.com Or visit our web
#include "../src/utils/parseconfig.hpp"
#include "net/http_client_plus.h"
#include "core/entry.h"
#include "zip.h"
@@ -33,6 +33,7 @@ bool exsit_install_software(std::string name){
int result = std::system(command.c_str());
return (result == 0);
};
#ifndef _WIN32
std::pair<int,std::string> execute_command(const std::string& command){
std::array<char, 128> buffer;
std::string result;
@@ -52,6 +53,7 @@ std::pair<int,std::string> execute_command(const std::string& command){
return std::make_pair(return_code, command+"\n"+result);
};
#endif
@@ -77,15 +79,20 @@ fastweb::fastweb(const std::vector<std::string> &param)
std::cout << "|------------------------[FASTWEB]----------------------------" << std::endl;
std::cout << "| 软件版本:\t"<< FASTWEB_VERSION << std::endl;
std::cout << "| 工作目录:\t"<< strutils::replace(system::current_dir(),'\\','/') << std::endl;
#ifdef _WIN32
std::cout << "| Windows平台推荐使用网站管理器部署https://fwlua.com/thread-3-1-1.html"<<std::endl;
#endif
std::cout << "| 启动参数" << std::endl;
for (int i = 0; i < param.size(); i++)
{
std::cout << "|\t" << param[i] << std::endl;
}
std::cout << "|" << std::endl;
std::cout << "|" << std::endl;
ylib::println("| 重要提示:\t创建项目后请务必安装 `fastwebcore` 模块", ylib::ConsoleTextColor::RED);
#ifdef _WIN32
ylib::println("| Windows平台推荐使用网站管理器部署https://fwlua.com/thread-3-1-1.html", ylib::ConsoleTextColor::BLUE);
#endif
std::cout << "|----------------------------------------------------------------" << std::endl;
@@ -326,7 +333,7 @@ bool fastweb::module_infos(std::vector<fastweb::module_info>& list)
info.desc = td[k]["desc"].to<std::string>();
info.doc = td[k]["doc"].to<std::string>();
info.type = types[i];
info.download_win64 = td[k]["doc"].to<std::string>();
info.download_win64 = td[k]["download"]["win64"].to<std::string>();
info.download_linux_url = td[k]["download"]["linux"]["url"].to<std::string>();
info.download_linux_type = td[k]["download"]["linux"]["type"].to<std::string>();
@@ -341,7 +348,7 @@ void fastweb::wait()
while(true)
system::sleep_msec(1000*60);
}
#ifndef _WIN32
void fastweb::install_module_linux(fastweb::module_info info)
{
@@ -439,7 +446,7 @@ void fastweb::install_module_linux(fastweb::module_info info)
}
}
#else
void fastweb::install_module_windows(fastweb::module_info info)
{
@@ -456,26 +463,17 @@ void fastweb::install_module_windows(fastweb::module_info info)
return;
}
std::string zip_filepath = ylib::file::temp_filepath()+".zip";
ylib:file::write(zip_filepath,client.response());
ylib::file::write(zip_filepath,client.response());
std::string new_module_dirpath = m_ini.read("scripts","module_dir")+"/.install/"+info.id;
ylib::file::create_dir(new_module_dirpath,true);
std::string cmd = "unzip -o -q "+zip_filepath+" -d "+new_module_dirpath;
auto [code,err] = execute_command(cmd);
if(code != 0)
{
std::cerr<<"安装失败:"<<std::endl;
std::cerr<<cmd<<std::endl;
std::cerr<<err<<std::endl;
}
else
{
std::cerr<<"安装成功"<<std::endl;
}
}
zip_extract(zip_filepath.c_str(), new_module_dirpath.c_str(), nullptr, nullptr);
std::cerr<<"安装成功"<<std::endl;
}
#endif
bool fastweb::parse_config(const std::string &ini_filepath)
{
auto [result,err] = parseconfig(m_ini,ini_filepath);

View File

@@ -3,7 +3,7 @@
#include <string>
#include "base/define.h"
#include "util/ini.h"
#define FASTWEB_VERSION "V1.0.5"
#define FASTWEB_VERSION "V1.0.6"
#define FASTWEB_MODULE_JSON_URL "https://download.fwlua.com/module/module.json"
class fastweb
{
@@ -61,10 +61,11 @@ private:
private:
/// @brief 无限等待
void wait();
#ifndef _WIN32
void install_module_linux(fastweb::module_info info);
#else
void install_module_windows(fastweb::module_info info);
#endif
/// @brief 解析INI配置文件

10081
tests/miniz.h Normal file

File diff suppressed because it is too large Load Diff

1784
tests/zip.cpp Normal file

File diff suppressed because it is too large Load Diff

447
tests/zip.h Normal file
View File

@@ -0,0 +1,447 @@
/*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#ifndef ZIP_H
#define ZIP_H
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#if !defined(_POSIX_C_SOURCE) && defined(_MSC_VER)
// 64-bit Windows is the only mainstream platform
// where sizeof(long) != sizeof(void*)
#ifdef _WIN64
typedef long long ssize_t; /* byte count or error */
#else
typedef long ssize_t; /* byte count or error */
#endif
#endif
#ifndef MAX_PATH
#define MAX_PATH 1024 /* # chars in a path name including NULL */
#endif
/**
* @mainpage
*
* Documenation for @ref zip.
*/
/**
* @addtogroup zip
* @{
*/
/**
* Default zip compression level.
*/
#define ZIP_DEFAULT_COMPRESSION_LEVEL 6
/**
* Error codes
*/
#define ZIP_ENOINIT -1 // not initialized
#define ZIP_EINVENTNAME -2 // invalid entry name
#define ZIP_ENOENT -3 // entry not found
#define ZIP_EINVMODE -4 // invalid zip mode
#define ZIP_EINVLVL -5 // invalid compression level
#define ZIP_ENOSUP64 -6 // no zip 64 support
#define ZIP_EMEMSET -7 // memset error
#define ZIP_EWRTENT -8 // cannot write data to entry
#define ZIP_ETDEFLINIT -9 // cannot initialize tdefl compressor
#define ZIP_EINVIDX -10 // invalid index
#define ZIP_ENOHDR -11 // header not found
#define ZIP_ETDEFLBUF -12 // cannot flush tdefl buffer
#define ZIP_ECRTHDR -13 // cannot create entry header
#define ZIP_EWRTHDR -14 // cannot write entry header
#define ZIP_EWRTDIR -15 // cannot write to central dir
#define ZIP_EOPNFILE -16 // cannot open file
#define ZIP_EINVENTTYPE -17 // invalid entry type
#define ZIP_EMEMNOALLOC -18 // extracting data using no memory allocation
#define ZIP_ENOFILE -19 // file not found
#define ZIP_ENOPERM -20 // no permission
#define ZIP_EOOMEM -21 // out of memory
#define ZIP_EINVZIPNAME -22 // invalid zip archive name
#define ZIP_EMKDIR -23 // make dir error
#define ZIP_ESYMLINK -24 // symlink error
#define ZIP_ECLSZIP -25 // close archive error
#define ZIP_ECAPSIZE -26 // capacity size too small
#define ZIP_EFSEEK -27 // fseek error
#define ZIP_EFREAD -28 // fread error
#define ZIP_EFWRITE -29 // fwrite error
/**
* Looks up the error message string coresponding to an error number.
* @param errnum error number
* @return error message string coresponding to errnum or NULL if error is not
* found.
*/
const char *zip_strerror(int errnum);
/**
* @struct zip_t
*
* This data structure is used throughout the library to represent zip archive -
* forward declaration.
*/
struct zip_t;
/**
* Opens zip archive with compression level using the given mode.
*
* @param zipname zip archive file name.
* @param level compression level (0-9 are the standard zlib-style levels).
* @param mode file access mode.
* - 'r': opens a file for reading/extracting (the file must exists).
* - 'w': creates an empty file for writing.
* - 'a': appends to an existing archive.
*
* @return the zip archive handler or NULL on error
*/
struct zip_t *zip_open(const char *zipname, int level,
char mode);
/**
* Closes the zip archive, releases resources - always finalize.
*
* @param zip zip archive handler.
*/
void zip_close(struct zip_t *zip);
/**
* Determines if the archive has a zip64 end of central directory headers.
*
* @param zip zip archive handler.
*
* @return the return code - 1 (true), 0 (false), negative number (< 0) on
* error.
*/
int zip_is64(struct zip_t *zip);
/**
* Opens an entry by name in the zip archive.
*
* For zip archive opened in 'w' or 'a' mode the function will append
* a new entry. In readonly mode the function tries to locate the entry
* in global dictionary.
*
* @param zip zip archive handler.
* @param entryname an entry name in local dictionary.
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
int zip_entry_open(struct zip_t *zip, const char *entryname);
/**
* Opens an entry by name in the zip archive.
*
* For zip archive opened in 'w' or 'a' mode the function will append
* a new entry. In readonly mode the function tries to locate the entry
* in global dictionary (case sensitive).
*
* @param zip zip archive handler.
* @param entryname an entry name in local dictionary (case sensitive).
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
int zip_entry_opencasesensitive(struct zip_t *zip,
const char *entryname);
/**
* Opens a new entry by index in the zip archive.
*
* This function is only valid if zip archive was opened in 'r' (readonly) mode.
*
* @param zip zip archive handler.
* @param index index in local dictionary.
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
int zip_entry_openbyindex(struct zip_t *zip, size_t index);
/**
* Closes a zip entry, flushes buffer and releases resources.
*
* @param zip zip archive handler.
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
int zip_entry_close(struct zip_t *zip);
/**
* Returns a local name of the current zip entry.
*
* The main difference between user's entry name and local entry name
* is optional relative path.
* Following .ZIP File Format Specification - the path stored MUST not contain
* a drive or device letter, or a leading slash.
* All slashes MUST be forward slashes '/' as opposed to backwards slashes '\'
* for compatibility with Amiga and UNIX file systems etc.
*
* @param zip: zip archive handler.
*
* @return the pointer to the current zip entry name, or NULL on error.
*/
const char *zip_entry_name(struct zip_t *zip);
/**
* Returns an index of the current zip entry.
*
* @param zip zip archive handler.
*
* @return the index on success, negative number (< 0) on error.
*/
ssize_t zip_entry_index(struct zip_t *zip);
/**
* Determines if the current zip entry is a directory entry.
*
* @param zip zip archive handler.
*
* @return the return code - 1 (true), 0 (false), negative number (< 0) on
* error.
*/
int zip_entry_isdir(struct zip_t *zip);
/**
* Returns the uncompressed size of the current zip entry.
* Alias for zip_entry_uncomp_size (for backward compatibility).
*
* @param zip zip archive handler.
*
* @return the uncompressed size in bytes.
*/
unsigned long long zip_entry_size(struct zip_t *zip);
/**
* Returns the uncompressed size of the current zip entry.
*
* @param zip zip archive handler.
*
* @return the uncompressed size in bytes.
*/
unsigned long long zip_entry_uncomp_size(struct zip_t *zip);
/**
* Returns the compressed size of the current zip entry.
*
* @param zip zip archive handler.
*
* @return the compressed size in bytes.
*/
unsigned long long zip_entry_comp_size(struct zip_t *zip);
/**
* Returns CRC-32 checksum of the current zip entry.
*
* @param zip zip archive handler.
*
* @return the CRC-32 checksum.
*/
unsigned int zip_entry_crc32(struct zip_t *zip);
/**
* Compresses an input buffer for the current zip entry.
*
* @param zip zip archive handler.
* @param buf input buffer.
* @param bufsize input buffer size (in bytes).
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
int zip_entry_write(struct zip_t *zip, const void *buf,
size_t bufsize);
/**
* Compresses a file for the current zip entry.
*
* @param zip zip archive handler.
* @param filename input file.
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
int zip_entry_fwrite(struct zip_t *zip, const char *filename);
/**
* Extracts the current zip entry into output buffer.
*
* The function allocates sufficient memory for a output buffer.
*
* @param zip zip archive handler.
* @param buf output buffer.
* @param bufsize output buffer size (in bytes).
*
* @note remember to release memory allocated for a output buffer.
* for large entries, please take a look at zip_entry_extract function.
*
* @return the return code - the number of bytes actually read on success.
* Otherwise a negative number (< 0) on error.
*/
ssize_t zip_entry_read(struct zip_t *zip, void **buf,
size_t *bufsize);
/**
* Extracts the current zip entry into a memory buffer using no memory
* allocation.
*
* @param zip zip archive handler.
* @param buf preallocated output buffer.
* @param bufsize output buffer size (in bytes).
*
* @note ensure supplied output buffer is large enough.
* zip_entry_size function (returns uncompressed size for the current
* entry) can be handy to estimate how big buffer is needed.
* For large entries, please take a look at zip_entry_extract function.
*
* @return the return code - the number of bytes actually read on success.
* Otherwise a negative number (< 0) on error (e.g. bufsize is not large
* enough).
*/
ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf,
size_t bufsize);
/**
* Extracts the current zip entry into output file.
*
* @param zip zip archive handler.
* @param filename output file.
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
int zip_entry_fread(struct zip_t *zip, const char *filename);
/**
* Extracts the current zip entry using a callback function (on_extract).
*
* @param zip zip archive handler.
* @param on_extract callback function.
* @param arg opaque pointer (optional argument, which you can pass to the
* on_extract callback)
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
int
zip_entry_extract(struct zip_t *zip,
size_t (*on_extract)(void *arg, uint64_t offset,
const void *data, size_t size),
void *arg);
/**
* Returns the number of all entries (files and directories) in the zip archive.
*
* @param zip zip archive handler.
*
* @return the return code - the number of entries on success, negative number
* (< 0) on error.
*/
ssize_t zip_entries_total(struct zip_t *zip);
/**
* Deletes zip archive entries.
*
* @param zip zip archive handler.
* @param entries array of zip archive entries to be deleted.
* @param len the number of entries to be deleted.
* @return the number of deleted entries, or negative number (< 0) on error.
*/
ssize_t zip_entries_delete(struct zip_t *zip,
char *const entries[], size_t len);
/**
* Extracts a zip archive stream into directory.
*
* If on_extract is not NULL, the callback will be called after
* successfully extracted each zip entry.
* Returning a negative value from the callback will cause abort and return an
* error. The last argument (void *arg) is optional, which you can use to pass
* data to the on_extract callback.
*
* @param stream zip archive stream.
* @param size stream size.
* @param dir output directory.
* @param on_extract on extract callback.
* @param arg opaque pointer.
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
int
zip_stream_extract(const char *stream, size_t size, const char *dir,
int (*on_extract)(const char *filename, void *arg),
void *arg);
/**
* Opens zip archive stream into memory.
*
* @param stream zip archive stream.
* @param size stream size.
*
* @return the zip archive handler or NULL on error
*/
struct zip_t *zip_stream_open(const char *stream, size_t size,
int level, char mode);
/**
* Copy zip archive stream output buffer.
*
* @param zip zip archive handler.
* @param buf output buffer. User should free buf.
* @param bufsize output buffer size (in bytes).
*
* @return copy size
*/
ssize_t zip_stream_copy(struct zip_t *zip, void **buf,
size_t *bufsize);
/**
* Close zip archive releases resources.
*
* @param zip zip archive handler.
*
* @return
*/
void zip_stream_close(struct zip_t *zip);
/**
* Creates a new archive and puts files into a single zip archive.
*
* @param zipname zip archive file.
* @param filenames input files.
* @param len: number of input files.
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
int zip_create(const char *zipname, const char *filenames[],
size_t len);
/**
* Extracts a zip archive file into directory.
*
* If on_extract_entry is not NULL, the callback will be called after
* successfully extracted each zip entry.
* Returning a negative value from the callback will cause abort and return an
* error. The last argument (void *arg) is optional, which you can use to pass
* data to the on_extract_entry callback.
*
* @param zipname zip archive file.
* @param dir output directory.
* @param on_extract_entry on extract callback.
* @param arg opaque pointer.
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
int zip_extract(const char *zipname, const char *dir,
int (*on_extract_entry)(const char *filename,
void *arg),
void *arg);
#endif