Decompressing a zip containing files with Cyrillic names with lbarchive - c++

For my C++ project I need a library to decompress various formats of archives. I found libarchive was the best solution for this, however when I started testing it I ran into a problem. I tried running an untar example from the library over a zip containing files named in Russian and it just refuses to work.
archive_read_support_compression_all(a);
archive_read_support_format_all(a);
archive_read_open_filename(a, filename, 10240)));
for (;;) {
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_EOF) {
break;
}
if (r != ARCHIVE_OK) {
if (r == ARCHIVE_WARN) {
warn("archive_read_next_header()", archive_error_string(a)); // This warning goes off
} else {
fail("archive_read_next_header()", archive_error_string(a), 1);
}
}
msg(archive_entry_pathname(entry)); // entry is null
}
}
When I run this over other archive formats containing the same files everything is fime but for zip it fails with
archive_read_next_header() failed: Pathname cannot be converted from UTF-8 to current locale.
What can I do to make this work? I'm generally fine with the names being gibberish at this point
The system I'm running is Ubuntu 20.04 if that's important

Related

Reading txt file in Visual studio 2019 C++

I am trying to open a file for this program. I have tried pathing it directly using examples like C:\User... but for some reason it still says it can't find the file. I have looked on the internet as well as youtube to open files using C++ and its pretty straight forward. However I still can't get this .txt file to be read. Maybe it has to do with Visual Studio?
FILE* in_fp, * fopen();
int main() {
if ((in_fp = fopen("TextFile.txt", "r")) == NULL)
printf("ERROR - cannot open front.in \n");
else
{
getChar();
do {
lex();
} while (nextToken != EOF);
}
}

Steam Protocol C++ Unzip Multi message

I'm writting a plugin for Steam protocol in C++. I'm using https://github.com/seishun/SteamPP which uses protobufs from https://github.com/SteamRE/SteamKit and generally it works. I can communicate Steam, I can send and receive single messages (including logging in) without problems, but Steam sends often few messages zipped in one message (EMsg::Multi from protobuf) and here is my problem. I cannot unzip them correctly. I can't understand what I'm doing wrong.
std::string unzip(std::string &input) {
auto archive = archive_read_new();
auto result = archive_read_support_filter_all(archive);
assert(result == ARCHIVE_OK);
result = archive_read_support_format_zip(archive);
assert(result == ARCHIVE_OK);
result = archive_read_open_memory(archive, &input[0], input.size());
assert(result == ARCHIVE_OK);
archive_entry* entry;
result = archive_read_next_header(archive, &entry);
if (result != ARCHIVE_OK) {
return "read next header error " + std::to_string(result);
}
assert(result == ARCHIVE_OK);
std::string output;
output.resize(archive_entry_size(entry));
auto length = archive_read_data(archive, &output[0], output.size());
assert(length == output.size());
if (length != output.size()) {
return "hello world" + std::to_string(length);
}
assert(archive_read_next_header(archive, &entry) == ARCHIVE_EOF);
result = archive_read_free(archive);
assert(result == ARCHIVE_OK);
return output;
}
in this function (libarchive) archive_read_data returns -25 which is an error code and next assert throws error. What is wrong? It's working well in C# SteamKit version and also in node.js version. I have tried also Crypto++ Gunzip but it throws an CryptoPP::Gunzip::HeaderErr exception.
CHECK DEBUG GIF
I think you are missing Zlib in your Libarchive, because Steam messages are deflated and you need Zlib to process them. Now libarchive couldn't process them so it returns -25 because of unsupported file type. Try to recompile libarchive with Zlib attached in CMake.

libzip: validating a zip file before loading

I use libzip to open zip files in my application and in order to ensure good behavior in case of corrupt zip files I manually corrupted a zip file (by removing a few random lines with a text editor) and try to load that file. However this hangs the entire app because zip_fread() never returns.
Is there a where to determine if a zip file is valid before loading it to avoid such situations?
Update
The behavior seems to depend on the version, so I probably only need to update. This is the code I use on Windows, Mac OS and Linux:
int err;
zip *z= zip_open(zipfile.c_str(), 0, &err);
if (!z)
{
if (err == ZIP_ER_NOZIP)
throw std::runtime_error("The file is not a Workbench document.");
else if (err == ZIP_ER_MEMORY)
throw grt::os_error("Cannot allocate enough memory to open document.");
else if (err == ZIP_ER_NOENT)
throw grt::os_error("File not found.");
int len= zip_error_to_str(NULL, 0, 0, err);
std::string msg;
if (len > 0)
{
char *buf= (char*)g_malloc(len+1);
zip_error_to_str(buf, len+1, 0, err);
msg= buf;
g_free(buf);
}
else
msg= "error opening zip archive";
zip_close(z);
throw std::runtime_error(strfmt(_("Cannot open document file: %s"), msg.c_str()));
}
On OS X this fragment does not return an error (I used the same file for all platforms). Instead the following zip_read() call just hangs. On the other platforms zip_read() immediately returns with a result < 0, so it's easy to catch the error there.

How to handle JPEG #41 error

I have a project in C++ Builder6. There's an OpenDialog where I upload images to the project. I'd like my project to be safe and because it only accepts .jpg or .bmp images I decided to make a restriction. As far as I'm concerned I can recognize a .jpg file by setting my stream reader to the 4th position. If I find "JFIF" here, It'll be .jpeg file. And so on.
Here's my code
if(OpenDialog1->Execute())
{
TFileStream *stream = new TFileStream(OpenDialog1->FileName, fmOpenRead);
if(stream != NULL)
{
if(stream->Size < 10)
{
delete stream;
return;
}
char str[10];
stream->Read(str, 10);
if(AnsiString(str + 6).SetLength(4)=="JFIF")
{
ShowMessage("It's jpeg");
}
else if ( AnsiString(str).SetLength(2)=="BM") {
ShowMessage("It's bmp");
}
else
{
ShowMessage("It can not be downloaded");
return;
}
}
delete stream;
}
But unfortunately that code raises an exception about JPEG error #41 when I put here a text file with renamed extension.
So my idea doesn't work. The whole question is:
Can I make my program return my error messages without using try-catch method?
By the way, I understand why the exception is being raised, because my jpeg file is empty. But I'd like to handle it using my own system, not the standart exception.

libarchive - Extract to specified directory

I have a tar file I want to extract with libarchive to a specific directory. How can I make libarchive extract into to any directory I want? At the moment it always extracts into my program's working directory. I looked at this answer but all this does is change the location of an archive entry within the archive, i.e. it still extracts into my program's working directory just at a different sub-directory.
I resolved this issue next way:
(insert this code before calling 'archive_write_header' function)
const char* currentFile = archive_entry_pathname(archiveEntry);
const std::string fullOutputPath = destination + currentFile;
archive_entry_set_pathname(archiveEntry, fullOutputPath.c_str());
where destination is output path.
And it works.
From the libarchive discussion boards:
"It depends, of course, on the archive being extracted.
Typically, you would chdir() to the directory where you want the output to go, then use code similar to that in the Wiki Examples page:
A Complete Extractor Example
or in the untar.c sample program:
untar Example
Of course, if the tar file you're extracting has
interesting filenames (such as "c:\someotherdirectory"),
then you'll need to play with the filenames as you extract.
Note that the examples all use archive_read_next_header()
to get an entry object from the input archive describing
the next entry; you are then free to edit that entry description
in any way you wish -- in particular, you can change the
name, owner, or permissions -- before calling
archive_write_header() to recreate the entry on
disk.
The Examples page in the Wiki above is probably the
best place to start."
A longer C version of Alex's answer. To extract files to a temp_dir, use archive_entry_pathname() and archive_entry_set_pathname() to re-write each entry::
char* dest_file;
for (;;) {
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_EOF)
break;
if (r < ARCHIVE_OK)
fprintf(stderr, "%s\n", archive_error_string(a));
if (r < ARCHIVE_WARN)
return NULL;
asprintf(&dest_file, "%s/%s", temp_dir, archive_entry_pathname(entry));
archive_entry_set_pathname(entry, dest_file);
// printf(" writing %s\n", dest_file);
r = archive_write_header(ext, entry);
if (r < ARCHIVE_OK)
fprintf(stderr, "%s\n", archive_error_string(ext));
else if (archive_entry_size(entry) > 0) {
r = copy_data(a, ext);
if (r < ARCHIVE_OK)
fprintf(stderr, "%s\n", archive_error_string(ext));
if (r < ARCHIVE_WARN)
exit(1);
}
r = archive_write_finish_entry(ext);
if (r < ARCHIVE_OK)
fprintf(stderr, "%s\n", archive_error_string(ext));
if (r < ARCHIVE_WARN)
return NULL;
free(dest_file);
}