@@ -0,0 +1,320 @@
#include <curl/curl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <zlib.h>
#include <libgen.h>

#include "common.h"
#include "partial.h"

static size_t dummyReceive(void* data, size_t size, size_t nmemb, void* info) {
return size * nmemb;
}

static size_t receiveCentralDirectoryEnd(void* data, size_t size, size_t nmemb, ZipInfo* info) {
memcpy(info->centralDirectoryEnd + info->centralDirectoryEndRecvd, data, size * nmemb);
info->centralDirectoryEndRecvd += size * nmemb;
return size * nmemb;
}

static size_t receiveCentralDirectory(void* data, size_t size, size_t nmemb, ZipInfo* info) {
memcpy(info->centralDirectory + info->centralDirectoryRecvd, data, size * nmemb);
info->centralDirectoryRecvd += size * nmemb;
return size * nmemb;
}

static size_t receiveData(void* data, size_t size, size_t nmemb, void** pFileData) {
memcpy(pFileData[0], data, size * nmemb);
pFileData[0] = ((char*)pFileData[0]) + (size * nmemb);
ZipInfo* info = ((ZipInfo*)pFileData[1]);
CDFile* file = ((CDFile*)pFileData[2]);
size_t* progress = ((size_t*)pFileData[3]);

if(progress) {
*progress += size * nmemb;
}

if(info && info->progressCallback && file) {
info->progressCallback(info, file, *progress);
}

return size * nmemb;
}

static CDFile* flipFiles(ZipInfo* info)
{
char* cur = info->centralDirectory;

unsigned int i;
for(i = 0; i < info->centralDirectoryDesc->CDEntries; i++)
{
CDFile* candidate = (CDFile*) cur;
FLIPENDIANLE(candidate->signature);
FLIPENDIANLE(candidate->version);
FLIPENDIANLE(candidate->versionExtract);
// FLIPENDIANLE(candidate->flags);
FLIPENDIANLE(candidate->method);
FLIPENDIANLE(candidate->modTime);
FLIPENDIANLE(candidate->modDate);
// FLIPENDIANLE(candidate->crc32);
FLIPENDIANLE(candidate->compressedSize);
FLIPENDIANLE(candidate->size);
FLIPENDIANLE(candidate->lenFileName);
FLIPENDIANLE(candidate->lenExtra);
FLIPENDIANLE(candidate->lenComment);
FLIPENDIANLE(candidate->diskStart);
// FLIPENDIANLE(candidate->internalAttr);
// FLIPENDIANLE(candidate->externalAttr);
FLIPENDIANLE(candidate->offset);

cur += sizeof(CDFile) + candidate->lenFileName + candidate->lenExtra + candidate->lenComment;
}
}

ZipInfo* PartialZipInit(const char* url)
{
ZipInfo* info = (ZipInfo*) malloc(sizeof(ZipInfo));
info->url = strdup(url);
info->centralDirectoryRecvd = 0;
info->centralDirectoryEndRecvd = 0;
info->centralDirectoryDesc = NULL;
info->progressCallback = NULL;

info->hCurl = curl_easy_init();

curl_easy_setopt(info->hCurl, CURLOPT_URL, info->url);
curl_easy_setopt(info->hCurl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(info->hCurl, CURLOPT_NOBODY, 1);
curl_easy_setopt(info->hCurl, CURLOPT_WRITEFUNCTION, dummyReceive);

if(strncmp(info->url, "file://", 7) == 0)
{
char* filePath = (char*) curl_easy_unescape(info->hCurl, info->url + 7, 0, NULL);
FILE* f = fopen(filePath, "rb");
if(!f)
{
curl_free(filePath);
curl_easy_cleanup(info->hCurl);
free(info->url);
free(info);

return NULL;
}

fseek(f, 0, SEEK_END);
info->length = ftell(f);
fclose(f);

curl_free(filePath);
}
else
{
curl_easy_perform(info->hCurl);

double dFileLength;
curl_easy_getinfo(info->hCurl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dFileLength);
info->length = dFileLength;
}

char sRange[100];
uint64_t start;

if(info->length > (0xffff + sizeof(EndOfCD)))
start = info->length - 0xffff - sizeof(EndOfCD);
else
start = 0;

uint64_t end = info->length - 1;

sprintf(sRange, "%" PRIu64 "-%" PRIu64, start, end);

curl_easy_setopt(info->hCurl, CURLOPT_WRITEFUNCTION, receiveCentralDirectoryEnd);
curl_easy_setopt(info->hCurl, CURLOPT_WRITEDATA, info);
curl_easy_setopt(info->hCurl, CURLOPT_RANGE, sRange);
curl_easy_setopt(info->hCurl, CURLOPT_HTTPGET, 1);

curl_easy_perform(info->hCurl);

char* cur;
for(cur = info->centralDirectoryEnd; cur < (info->centralDirectoryEnd + (end - start - 1)); cur++)
{
EndOfCD* candidate = (EndOfCD*) cur;
uint32_t signature = candidate->signature;
FLIPENDIANLE(signature);
if(signature == 0x06054b50)
{
uint16_t lenComment = candidate->lenComment;
FLIPENDIANLE(lenComment);
if((cur + lenComment + sizeof(EndOfCD)) == (info->centralDirectoryEnd + info->centralDirectoryEndRecvd))
{
FLIPENDIANLE(candidate->diskNo);
FLIPENDIANLE(candidate->CDDiskNo);
FLIPENDIANLE(candidate->CDDiskEntries);
FLIPENDIANLE(candidate->CDEntries);
FLIPENDIANLE(candidate->CDSize);
FLIPENDIANLE(candidate->CDOffset);
FLIPENDIANLE(candidate->lenComment);
info->centralDirectoryDesc = candidate;
break;
}
}

}

if(info->centralDirectoryDesc)
{
info->centralDirectory = malloc(info->centralDirectoryDesc->CDSize);
start = info->centralDirectoryDesc->CDOffset;
end = start + info->centralDirectoryDesc->CDSize - 1;
sprintf(sRange, "%" PRIu64 "-%" PRIu64, start, end);
curl_easy_setopt(info->hCurl, CURLOPT_WRITEFUNCTION, receiveCentralDirectory);
curl_easy_setopt(info->hCurl, CURLOPT_WRITEDATA, info);
curl_easy_setopt(info->hCurl, CURLOPT_RANGE, sRange);
curl_easy_setopt(info->hCurl, CURLOPT_HTTPGET, 1);
curl_easy_perform(info->hCurl);

flipFiles(info);

return info;
}
else
{
curl_easy_cleanup(info->hCurl);
free(info->url);
free(info);
return NULL;
}
}

CDFile* PartialZipFindFile(ZipInfo* info, const char* fileName)
{
char* cur = info->centralDirectory;
unsigned int i;
for(i = 0; i < info->centralDirectoryDesc->CDEntries; i++)
{
CDFile* candidate = (CDFile*) cur;
const char* curFileName = cur + sizeof(CDFile);

if(strlen(fileName) == candidate->lenFileName && strncmp(fileName, curFileName, candidate->lenFileName) == 0)
return candidate;

cur += sizeof(CDFile) + candidate->lenFileName + candidate->lenExtra + candidate->lenComment;
}

return NULL;
}

CDFile* PartialZipListFiles(ZipInfo* info)
{
char* cur = info->centralDirectory;
unsigned int i;
for(i = 0; i < info->centralDirectoryDesc->CDEntries; i++)
{
CDFile* candidate = (CDFile*) cur;
const char* curFileName = cur + sizeof(CDFile);
char* myFileName = (char*) malloc(candidate->lenFileName + 1);
memcpy(myFileName, curFileName, candidate->lenFileName);
myFileName[candidate->lenFileName] = '\0';

printf("%s: method: %d, compressed size: %d, size: %d\n", myFileName, candidate->method,
candidate->compressedSize, candidate->size);

free(myFileName);

cur += sizeof(CDFile) + candidate->lenFileName + candidate->lenExtra + candidate->lenComment;
}

return NULL;
}

unsigned char* PartialZipGetFile(ZipInfo* info, CDFile* file, char* sizeToDownload)
{
LocalFile localHeader;
LocalFile* pLocalHeader = &localHeader;

uint64_t start = file->offset;
uint64_t end = file->offset + sizeof(LocalFile) - 1;
char sRange[100];
sprintf(sRange, "%" PRIu64 "-%" PRIu64, start, end);

void* pFileHeader[] = {pLocalHeader, NULL, NULL, NULL};

curl_easy_setopt(info->hCurl, CURLOPT_URL, info->url);
curl_easy_setopt(info->hCurl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(info->hCurl, CURLOPT_WRITEFUNCTION, receiveData);
curl_easy_setopt(info->hCurl, CURLOPT_WRITEDATA, &pFileHeader);
curl_easy_setopt(info->hCurl, CURLOPT_RANGE, sRange);
curl_easy_setopt(info->hCurl, CURLOPT_HTTPGET, 1);
curl_easy_perform(info->hCurl);

FLIPENDIANLE(localHeader.signature);
FLIPENDIANLE(localHeader.versionExtract);
// FLIPENDIANLE(localHeader.flags);
FLIPENDIANLE(localHeader.method);
FLIPENDIANLE(localHeader.modTime);
FLIPENDIANLE(localHeader.modDate);
// FLIPENDIANLE(localHeader.crc32);
FLIPENDIANLE(localHeader.compressedSize);
FLIPENDIANLE(localHeader.size);
FLIPENDIANLE(localHeader.lenFileName);
FLIPENDIANLE(localHeader.lenExtra);

if(sizeToDownload != NULL) {
file->compressedSize = atoi(sizeToDownload);
file->size = atoi(sizeToDownload);
}

unsigned char* fileData = (unsigned char*) malloc(file->compressedSize);
size_t progress = 0;
void* pFileData[] = {fileData, info, file, &progress};

start = file->offset + sizeof(LocalFile) + localHeader.lenFileName + localHeader.lenExtra;
end = start + file->compressedSize - 1;
sprintf(sRange, "%" PRIu64 "-%" PRIu64, start, end);

curl_easy_setopt(info->hCurl, CURLOPT_WRITEFUNCTION, receiveData);
curl_easy_setopt(info->hCurl, CURLOPT_WRITEDATA, pFileData);
curl_easy_setopt(info->hCurl, CURLOPT_RANGE, sRange);
curl_easy_setopt(info->hCurl, CURLOPT_HTTPGET, 1);
curl_easy_perform(info->hCurl);

if(file->method == 8)
{
unsigned char* uncData = (unsigned char*) malloc(file->size);
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = NULL;

inflateInit2(&strm, -MAX_WBITS);
strm.avail_in = file->compressedSize;
strm.next_in = fileData;
strm.avail_out = file->size;
strm.next_out = uncData;
inflate(&strm, Z_FINISH);
inflateEnd(&strm);
free(fileData);
fileData = uncData;
}
return fileData;
}

void PartialZipSetProgressCallback(ZipInfo* info, PartialZipProgressCallback progressCallback)
{
info->progressCallback = progressCallback;
}

void PartialZipRelease(ZipInfo* info)
{
curl_easy_cleanup(info->hCurl);
free(info->centralDirectory);
free(info->url);
free(info);

curl_global_cleanup();
}

@@ -0,0 +1,84 @@
#include <inttypes.h>
#include <curl/curl.h>

typedef struct EndOfCD {
uint32_t signature;
uint16_t diskNo;
uint16_t CDDiskNo;
uint16_t CDDiskEntries;
uint16_t CDEntries;
uint32_t CDSize;
uint32_t CDOffset;
uint16_t lenComment;
} __attribute__ ((packed)) EndOfCD;

typedef struct CDFile {
uint32_t signature;
uint16_t version;
uint16_t versionExtract;
uint16_t flags;
uint16_t method;
uint16_t modTime;
uint16_t modDate;
uint32_t crc32;
uint32_t compressedSize;
uint32_t size;
uint16_t lenFileName;
uint16_t lenExtra;
uint16_t lenComment;
uint16_t diskStart;
uint16_t internalAttr;
uint32_t externalAttr;
uint32_t offset;
} __attribute__ ((packed)) CDFile;

typedef struct LocalFile {
uint32_t signature;
uint16_t versionExtract;
uint16_t flags;
uint16_t method;
uint16_t modTime;
uint16_t modDate;
uint32_t crc32;
uint32_t compressedSize;
uint32_t size;
uint16_t lenFileName;
uint16_t lenExtra;
} __attribute__ ((packed)) LocalFile;

typedef struct ZipInfo ZipInfo;

typedef void (*PartialZipProgressCallback)(ZipInfo* info, CDFile* file, size_t progress);

struct ZipInfo {
char* url;
uint64_t length;
CURL* hCurl;
char* centralDirectory;
size_t centralDirectoryRecvd;
EndOfCD* centralDirectoryDesc;
char centralDirectoryEnd[0xffff + sizeof(EndOfCD)];
size_t centralDirectoryEndRecvd;
PartialZipProgressCallback progressCallback;
};

#ifdef __cplusplus
extern "C" {
#endif

int partial_download(char* url, char* remotefile, char* localpath);
ZipInfo* PartialZipInit(const char* url);

CDFile* PartialZipFindFile(ZipInfo* info, const char* fileName);

CDFile* PartialZipListFiles(ZipInfo* info);

unsigned char* PartialZipGetFile(ZipInfo* info, CDFile* file, char* sizeToDownload);

void PartialZipRelease(ZipInfo* info);

void PartialZipSetProgressCallback(ZipInfo* info, PartialZipProgressCallback progressCallback);

#ifdef __cplusplus
}
#endif
@@ -0,0 +1,58 @@
#include <stdio.h>
#include "common.h"
#include "partial.h"

void callback(ZipInfo* info, CDFile* file, size_t progress) {
int percentDone = progress * 100/file->compressedSize;
printf("\rDownloading: %d%%", percentDone);
}


int partial_download(char* url, char* remotefile, char* localpath){
char* sizeToDownload;

printf("Initializing download for %s...\n", remotefile);

ZipInfo* info = PartialZipInit(url);
if(!info)
{
printf("Cannot find %s\n", url);
return 0;
}

PartialZipSetProgressCallback(info, callback);

CDFile* file = PartialZipFindFile(info, remotefile);
if(!file)
{
printf("Cannot find %s in %s\n", remotefile, url);
return 0;
}

unsigned char* data = PartialZipGetFile(info, file, sizeToDownload);
int dataLen = file->size;

PartialZipRelease(info);

printf("\n");

data = realloc(data, dataLen + 1);
data[dataLen] = '\0';

FILE* out = fopen(localpath, "wb");
if (out == NULL)
{
printf("Failed to open %s", localpath);
return 1;
}

fwrite(data, sizeof(char), dataLen, out);

fclose(out);

free(data);

return 0;
}

//startshit("https://cydia.saurik.com/api/latest/2", "Impactor.exe", "test");