vdr-plugin-scraper2vdr/lib/common.c

1205 lines
27 KiB
C
Raw Normal View History

2014-04-12 17:10:43 +02:00
/*
* common.c:
*
* See the README file for copyright information and how to reach the author.
*
*/
#include <sys/stat.h>
2014-05-09 01:05:21 +02:00
#include <sys/time.h>
2014-04-12 17:10:43 +02:00
#ifdef USEUUID
# include <uuid/uuid.h>
#endif
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <zlib.h>
2014-05-09 01:05:21 +02:00
#include <errno.h>
2014-04-12 17:10:43 +02:00
#ifdef USELIBARCHIVE
# include <archive.h>
# include <archive_entry.h>
#endif
#ifdef VDR_PLUGIN
# include <vdr/thread.h>
#endif
#include "common.h"
#include "config.h"
#ifdef VDR_PLUGIN
cMutex logMutex;
#endif
//***************************************************************************
// Debug
//***************************************************************************
void tell(int eloquence, const char* format, ...)
{
if (EPG2VDRConfig.loglevel < eloquence)
return ;
const int sizeBuffer = 100000;
char t[sizeBuffer+100]; *t = 0;
va_list ap;
#ifdef VDR_PLUGIN
cMutexLock lock(&logMutex);
#endif
va_start(ap, format);
#ifdef VDR_PLUGIN
2014-05-09 01:05:21 +02:00
snprintf(t, sizeBuffer, LOG_PREFIX);
2014-04-12 17:10:43 +02:00
#endif
vsnprintf(t+strlen(t), sizeBuffer-strlen(t), format, ap);
if (EPG2VDRConfig.logstdout)
{
char buf[50+TB];
2014-05-09 01:05:21 +02:00
timeval tp;
gettimeofday(&tp, 0);
tm* tm = localtime(&tp.tv_sec);
sprintf(buf,"%2.2d:%2.2d:%2.2d,%3.3ld ",
tm->tm_hour, tm->tm_min, tm->tm_sec,
tp.tv_usec / 1000);
2014-04-12 17:10:43 +02:00
printf("%s %s\n", buf, t);
2014-05-09 01:05:21 +02:00
2014-04-12 17:10:43 +02:00
}
else
syslog(LOG_ERR, "%s", t);
va_end(ap);
}
2014-05-09 01:05:21 +02:00
//***************************************************************************
// Save Realloc
//***************************************************************************
char* srealloc(void* ptr, size_t size)
{
void* n = realloc(ptr, size);
if (!n)
{
free(ptr);
ptr = 0;
}
return (char*)n;
}
//***************************************************************************
// us now
//***************************************************************************
double usNow()
{
struct timeval tp;
gettimeofday(&tp, 0);
return tp.tv_sec * 1000000.0 + tp.tv_usec;
}
2014-04-12 17:10:43 +02:00
//***************************************************************************
// Host ID
//***************************************************************************
unsigned int getHostId()
{
static unsigned int id = gethostid() & 0xFFFFFFFF;
return id;
}
//***************************************************************************
// String Operations
//***************************************************************************
void toUpper(std::string& str)
{
const char* s = str.c_str();
int lenSrc = str.length();
char* dest = (char*)malloc(lenSrc+TB); *dest = 0;
char* d = dest;
int csSrc; // size of character
for (int ps = 0; ps < lenSrc; ps += csSrc)
{
csSrc = max(mblen(&s[ps], lenSrc-ps), 1);
if (csSrc == 1)
*d++ = toupper(s[ps]);
else if (csSrc == 2 && s[ps] == (char)0xc3 && s[ps+1] >= (char)0xa0)
{
*d++ = s[ps];
*d++ = s[ps+1] - 32;
}
else
{
for (int i = 0; i < csSrc; i++)
*d++ = s[ps+i];
}
}
*d = 0;
str = dest;
free(dest);
}
2014-05-09 01:05:21 +02:00
//***************************************************************************
// To Case (UTF-8 save)
//***************************************************************************
const char* toCase(Case cs, char* str)
{
char* s = str;
int lenSrc = strlen(str);
int csSrc; // size of character
for (int ps = 0; ps < lenSrc; ps += csSrc)
{
csSrc = max(mblen(&s[ps], lenSrc-ps), 1);
if (csSrc == 1)
s[ps] = cs == cUpper ? toupper(s[ps]) : tolower(s[ps]);
else if (csSrc == 2 && s[ps] == (char)0xc3 && s[ps+1] >= (char)0xa0)
{
s[ps] = s[ps];
s[ps+1] = cs == cUpper ? toupper(s[ps+1]) : tolower(s[ps+1]);
}
else
{
for (int i = 0; i < csSrc; i++)
s[ps+i] = s[ps+i];
}
}
return str;
}
2014-04-12 17:10:43 +02:00
void removeChars(std::string& str, const char* ignore)
{
const char* s = str.c_str();
int lenSrc = str.length();
int lenIgn = strlen(ignore);
char* dest = (char*)malloc(lenSrc+TB); *dest = 0;
char* d = dest;
int csSrc; // size of character
int csIgn; //
for (int ps = 0; ps < lenSrc; ps += csSrc)
{
int skip = no;
csSrc = max(mblen(&s[ps], lenSrc-ps), 1);
for (int pi = 0; pi < lenIgn; pi += csIgn)
{
csIgn = max(mblen(&ignore[pi], lenIgn-pi), 1);
if (csSrc == csIgn && strncmp(&s[ps], &ignore[pi], csSrc) == 0)
{
skip = yes;
break;
}
}
if (!skip)
{
for (int i = 0; i < csSrc; i++)
*d++ = s[ps+i];
}
}
*d = 0;
str = dest;
free(dest);
}
void removeCharsExcept(std::string& str, const char* except)
{
const char* s = str.c_str();
int lenSrc = str.length();
int lenIgn = strlen(except);
char* dest = (char*)malloc(lenSrc+TB); *dest = 0;
char* d = dest;
int csSrc; // size of character
int csIgn; //
for (int ps = 0; ps < lenSrc; ps += csSrc)
{
int skip = yes;
csSrc = max(mblen(&s[ps], lenSrc-ps), 1);
for (int pi = 0; pi < lenIgn; pi += csIgn)
{
csIgn = max(mblen(&except[pi], lenIgn-pi), 1);
if (csSrc == csIgn && strncmp(&s[ps], &except[pi], csSrc) == 0)
{
skip = no;
break;
}
}
if (!skip)
{
for (int i = 0; i < csSrc; i++)
*d++ = s[ps+i];
}
}
*d = 0;
str = dest;
free(dest);
}
void removeWord(std::string& pattern, std::string word)
{
size_t pos;
if ((pos = pattern.find(word)) != std::string::npos)
pattern.swap(pattern.erase(pos, word.length()));
}
//***************************************************************************
// String Manipulation
//***************************************************************************
void prepareCompressed(std::string& pattern)
{
// const char* ignore = " (),.;:-_+*!#?=&%$<>§/'`´@~\"[]{}";
const char* notignore = "ABCDEFGHIJKLMNOPQRSTUVWXYZßÖÄÜöäü0123456789";
toUpper(pattern);
removeWord(pattern, " TEIL ");
removeWord(pattern, " FOLGE ");
removeCharsExcept(pattern, notignore);
}
//***************************************************************************
// Left Trim
//***************************************************************************
char* lTrim(char* buf)
{
if (buf)
{
char *tp = buf;
while (*tp && strchr("\n\r\t ",*tp))
tp++;
memmove(buf, tp, strlen(tp) +1);
}
return buf;
}
//*************************************************************************
// Right Trim
//*************************************************************************
char* rTrim(char* buf)
{
if (buf)
{
char *tp = buf + strlen(buf);
while (tp >= buf && strchr("\n\r\t ",*tp))
tp--;
*(tp+1) = 0;
}
return buf;
}
//*************************************************************************
// All Trim
//*************************************************************************
char* allTrim(char* buf)
{
return lTrim(rTrim(buf));
}
//***************************************************************************
// Number to String
//***************************************************************************
std::string num2Str(int num)
{
char txt[16];
snprintf(txt, sizeof(txt), "%d", num);
return std::string(txt);
}
//***************************************************************************
// Long to Pretty Time
//***************************************************************************
std::string l2pTime(time_t t)
{
char txt[30];
tm* tmp = localtime(&t);
strftime(txt, sizeof(txt), "%d.%m.%Y %T", tmp);
return std::string(txt);
}
//***************************************************************************
// MS to Duration
//***************************************************************************
std::string ms2Dur(uint64_t t)
{
char txt[30];
int s = t / 1000;
int ms = t % 1000;
snprintf(txt, sizeof(txt), "%d.%03d seconds", s, ms);
return std::string(txt);
}
//***************************************************************************
// Char to Char-String
//***************************************************************************
const char* c2s(char c, char* buf)
{
sprintf(buf, "%c", c);
return buf;
}
2014-05-09 01:05:21 +02:00
//***************************************************************************
// Store To File
//***************************************************************************
int storeToFile(const char* filename, const char* data, int size)
{
FILE* fout;
if ((fout = fopen(filename, "w+")))
{
fwrite(data, sizeof(char), size, fout);
fclose(fout);
}
else
{
tell(0, "Error, can't store '%s' to filesystem '%s'", filename, strerror(errno));
return fail;
}
return success;
}
//***************************************************************************
// Load From File
//***************************************************************************
int loadFromFile(const char* infile, MemoryStruct* data)
{
FILE* fin;
struct stat sb;
data->clear();
if (!fileExists(infile))
{
tell(0, "File '%s' not found'", infile);
return fail;
}
if (stat(infile, &sb) < 0)
{
tell(0, "Can't get info of '%s', error was '%s'", infile, strerror(errno));
return fail;
}
if ((fin = fopen(infile, "r")))
{
const char* sfx = suffixOf(infile);
data->size = sb.st_size;
data->modTime = sb.st_mtime;
data->memory = (char*)malloc(data->size);
fread(data->memory, sizeof(char), data->size, fin);
fclose(fin);
sprintf(data->tag, "%ld", data->size);
if (strcmp(sfx, "gz") == 0)
sprintf(data->contentEncoding, "gzip");
if (strcmp(sfx, "js") == 0)
sprintf(data->contentType, "application/javascript");
else if (strcmp(sfx, "png") == 0 || strcmp(sfx, "jpg") == 0 || strcmp(sfx, "gif") == 0)
sprintf(data->contentType, "image/%s", sfx);
else if (strcmp(sfx, "ico") == 0)
strcpy(data->contentType, "image/x-icon");
else
sprintf(data->contentType, "text/%s", sfx);
}
else
{
tell(0, "Error, can't open '%s' for reading, error was '%s'", infile, strerror(errno));
return fail;
}
return success;
}
2014-04-12 17:10:43 +02:00
//***************************************************************************
// TOOLS
//***************************************************************************
int isEmpty(const char* str)
{
return !str || !*str;
}
2014-05-09 01:05:21 +02:00
const char* notNull(const char* str)
{
if (!str)
return "<null>";
return str;
}
int isZero(const char* str)
{
const char* p = str;
while (p && *p)
{
if (*p != '0')
return no;
p++;
}
return yes;
}
//***************************************************************************
//
//***************************************************************************
2014-04-12 17:10:43 +02:00
char* sstrcpy(char* dest, const char* src, int max)
{
if (!dest || !src)
return 0;
strncpy(dest, src, max);
dest[max-1] = 0;
return dest;
}
int isLink(const char* path)
{
struct stat sb;
if (lstat(path, &sb) == 0)
return S_ISLNK(sb.st_mode);
2014-05-09 01:05:21 +02:00
tell(0, "Error: Detecting state for '%s' failed, error was '%s'", path, strerror(errno));
2014-04-12 17:10:43 +02:00
return false;
}
2014-05-09 01:05:21 +02:00
const char* suffixOf(const char* path)
{
const char* p;
if (path && (p = strrchr(path, '.')))
return p+1;
return "";
}
2014-04-12 17:10:43 +02:00
int fileSize(const char* path)
{
struct stat sb;
if (lstat(path, &sb) == 0)
return sb.st_size;
2014-05-09 01:05:21 +02:00
tell(0, "Error: Detecting state for '%s' failed, error was '%s'", path, strerror(errno));
2014-04-12 17:10:43 +02:00
2014-05-09 01:05:21 +02:00
return 0;
}
time_t fileModTime(const char* path)
{
struct stat sb;
if (lstat(path, &sb) == 0)
return sb.st_mtime;
tell(0, "Error: Detecting state for '%s' failed, error was '%s'", path, strerror(errno));
return 0;
2014-04-12 17:10:43 +02:00
}
int fileExists(const char* path)
{
return access(path, F_OK) == 0;
}
int createLink(const char* link, const char* dest, int force)
{
if (!fileExists(link) || force)
{
// may be the link exists and point to a wrong or already deleted destination ...
// .. therefore we delete the link at first
unlink(link);
if (symlink(dest, link) != 0)
{
2014-05-09 01:05:21 +02:00
tell(0, "Failed to create symlink '%s', error was '%s'", link, strerror(errno));
2014-04-12 17:10:43 +02:00
return fail;
}
}
return success;
}
//***************************************************************************
// Remove File
//***************************************************************************
int removeFile(const char* filename)
{
int lnk = isLink(filename);
if (unlink(filename) != 0)
{
2014-05-09 01:05:21 +02:00
tell(0, "Can't remove file '%s', '%s'", filename, strerror(errno));
2014-04-12 17:10:43 +02:00
return 1;
}
tell(3, "Removed %s '%s'", lnk ? "link" : "file", filename);
return 0;
}
//***************************************************************************
// Check Dir
//***************************************************************************
int chkDir(const char* path)
{
struct stat fs;
if (stat(path, &fs) != 0 || !S_ISDIR(fs.st_mode))
{
tell(0, "Creating directory '%s'", path);
if (mkdir(path, ACCESSPERMS) == -1)
{
2014-05-09 01:05:21 +02:00
tell(0, "Can't create directory '%s'", strerror(errno));
2014-04-12 17:10:43 +02:00
return fail;
}
}
return success;
}
#ifdef USELIBXML
//***************************************************************************
// Load XSLT
//***************************************************************************
xsltStylesheetPtr loadXSLT(const char* name, const char* path, int utf8)
{
xsltStylesheetPtr stylesheet;
char* xsltfile;
asprintf(&xsltfile, "%s/%s-%s.xsl", path, name, utf8 ? "utf-8" : "iso-8859-1");
if ((stylesheet = xsltParseStylesheetFile((const xmlChar*)xsltfile)) == 0)
tell(0, "Error: Can't load xsltfile %s", xsltfile);
else
tell(0, "Info: Stylesheet '%s' loaded", xsltfile);
free(xsltfile);
return stylesheet;
}
#endif
//***************************************************************************
// Gnu Unzip
//***************************************************************************
int gunzip(MemoryStruct* zippedData, MemoryStruct* unzippedData)
{
const int growthStep = 1024;
z_stream stream = {0,0,0,0,0,0,0,0,0,0,0,Z_NULL,Z_NULL,Z_NULL};
unsigned int resultSize = 0;
int res = 0;
unzippedData->clear();
// determining the size in this way is taken from the sources of the gzip utility.
memcpy(&unzippedData->size, zippedData->memory + zippedData->size -4, 4);
unzippedData->memory = (char*)malloc(unzippedData->size);
// zlib initialisation
stream.avail_in = zippedData->size;
stream.next_in = (Bytef*)zippedData->memory;
stream.avail_out = unzippedData->size;
stream.next_out = (Bytef*)unzippedData->memory;
// The '+ 32' tells zlib to process zlib&gzlib headers
res = inflateInit2(&stream, MAX_WBITS + 32);
if (res != Z_OK)
{
tellZipError(res, " during zlib initialisation", stream.msg);
inflateEnd(&stream);
return fail;
}
// skip the header
res = inflate(&stream, Z_BLOCK);
if (res != Z_OK)
{
tellZipError(res, " while skipping the header", stream.msg);
inflateEnd(&stream);
return fail;
}
while (res == Z_OK)
{
if (stream.avail_out == 0)
{
unzippedData->size += growthStep;
unzippedData->memory = (char*)realloc(unzippedData->memory, unzippedData->size);
// Set the stream pointers to the potentially changed buffer!
stream.avail_out = resultSize - stream.total_out;
stream.next_out = (Bytef*)(unzippedData + stream.total_out);
}
res = inflate(&stream, Z_SYNC_FLUSH);
resultSize = stream.total_out;
}
if (res != Z_STREAM_END)
{
tellZipError(res, " during inflating", stream.msg);
inflateEnd(&stream);
return fail;
}
unzippedData->size = resultSize;
inflateEnd(&stream);
return success;
}
2014-05-09 01:05:21 +02:00
//***************************************************************************
// gzip
//***************************************************************************
int _gzip(Bytef* dest, uLongf* destLen, const Bytef* source, uLong sourceLen)
{
z_stream stream;
int res;
stream.next_in = (Bytef *)source;
stream.avail_in = (uInt)sourceLen;
stream.next_out = dest;
stream.avail_out = (uInt)*destLen;
if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
stream.zalloc = (alloc_func)0;
stream.zfree = (free_func)0;
stream.opaque = (voidpf)0;
if ((res = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY)) != Z_OK)
return res;
res = deflate(&stream, Z_FINISH);
if (res != Z_STREAM_END)
{
deflateEnd(&stream);
return res == Z_OK ? Z_BUF_ERROR : res;
}
*destLen = stream.total_out;
res = deflateEnd(&stream);
return res;
}
int gzip(MemoryStruct* data, MemoryStruct* zippedData)
{
int res;
uLong sizeMax = compressBound(data->size) + 512;
zippedData->clear();
zippedData->memory = (char*)malloc(sizeMax);
if ((res = _gzip((Bytef*)zippedData->memory, &sizeMax, (Bytef*)data->memory, data->size)) != Z_OK)
{
tellZipError(res, " during compression", "");
return fail;
}
zippedData->copyAttributes(data);
zippedData->size = sizeMax;
sprintf(zippedData->contentEncoding, "gzip");
return success;
}
2014-04-12 17:10:43 +02:00
//*************************************************************************
// tellZipError
//*************************************************************************
void tellZipError(int errorCode, const char* op, const char* msg)
{
if (!op) op = "";
if (!msg) msg = "None";
switch (errorCode)
{
case Z_OK: return;
case Z_STREAM_END: return;
2014-05-09 01:05:21 +02:00
case Z_MEM_ERROR: tell(0, "Error: Not enough memory to zip/unzip file%s!\n", op); return;
case Z_BUF_ERROR: tell(0, "Error: Couldn't zip/unzip data due to output buffer size problem%s!\n", op); return;
2014-04-12 17:10:43 +02:00
case Z_DATA_ERROR: tell(0, "Error: Zipped input data corrupted%s! Details: %s\n", op, msg); return;
case Z_STREAM_ERROR: tell(0, "Error: Invalid stream structure%s. Details: %s\n", op, msg); return;
2014-05-09 01:05:21 +02:00
default: tell(0, "Error: Couldn't zip/unzip data for unknown reason (%6d)%s!\n", errorCode, op); return;
2014-04-12 17:10:43 +02:00
}
}
//*************************************************************************
// Host Data
//*************************************************************************
#include <sys/utsname.h>
#include <netdb.h>
#include <ifaddrs.h>
static struct utsname info;
const char* getHostName()
{
// get info from kernel
if (uname(&info) == -1)
return "";
return info.nodename;
}
const char* getFirstIp()
{
struct ifaddrs *ifaddr, *ifa;
static char host[NI_MAXHOST] = "";
if (getifaddrs(&ifaddr) == -1)
{
tell(0, "getifaddrs() failed");
return "";
}
// walk through linked interface list
for (ifa = ifaddr; ifa; ifa = ifa->ifa_next)
{
if (!ifa->ifa_addr)
continue;
// For an AF_INET interfaces
if (ifa->ifa_addr->sa_family == AF_INET) // || ifa->ifa_addr->sa_family == AF_INET6)
{
int res = getnameinfo(ifa->ifa_addr,
(ifa->ifa_addr->sa_family == AF_INET) ? sizeof(struct sockaddr_in) :
sizeof(struct sockaddr_in6),
host, NI_MAXHOST, 0, 0, NI_NUMERICHOST);
if (res)
{
tell(0, "getnameinfo() failed: %s", gai_strerror(res));
return "";
}
// skip loopback interface
if (strcmp(host, "127.0.0.1") == 0)
continue;
tell(5, "%-8s %-15s %s", ifa->ifa_name, host,
ifa->ifa_addr->sa_family == AF_INET ? " (AF_INET)" :
ifa->ifa_addr->sa_family == AF_INET6 ? " (AF_INET6)" : "");
}
}
freeifaddrs(ifaddr);
return host;
}
#ifdef USELIBARCHIVE
//***************************************************************************
// unzip <file> and get data of first content which name matches <filter>
//***************************************************************************
int unzip(const char* file, const char* filter, char*& buffer, int& size, char* entryName)
{
const int step = 1024*10;
int bufSize = 0;
int r;
int res;
struct archive_entry* entry;
struct archive* a = archive_read_new();
*entryName = 0;
buffer = 0;
size = 0;
archive_read_support_filter_all(a);
archive_read_support_format_all(a);
r = archive_read_open_filename(a, file, 10204);
if (r != ARCHIVE_OK)
{
2014-05-09 01:05:21 +02:00
tell(0, "Error: Open '%s' failed - %s", file, strerror(errno));
2014-04-12 17:10:43 +02:00
return 1;
}
while (archive_read_next_header(a, &entry) == ARCHIVE_OK)
{
strcpy(entryName, archive_entry_pathname(entry));
if (strstr(entryName, filter))
{
bufSize = step;
buffer = (char*)malloc(bufSize+1);
while ((res = archive_read_data(a, buffer+size, step)) > 0)
{
size += res;
bufSize += step;
buffer = (char*)realloc(buffer, bufSize+1);
}
buffer[size] = 0;
break;
}
}
r = archive_read_free(a);
if (r != ARCHIVE_OK)
{
size = 0;
free(buffer);
return fail;
}
return size > 0 ? success : fail;
}
#endif
//***************************************************************************
// Class LogDuration
//***************************************************************************
#ifdef VDR_PLUGIN
# include <vdr/plugin.h>
LogDuration::LogDuration(const char* aMessage, int aLogLevel)
{
logLevel = aLogLevel;
strcpy(message, aMessage);
// at last !
durationStart = cTimeMs::Now();
}
LogDuration::~LogDuration()
{
2014-05-09 01:05:21 +02:00
tell(logLevel, "duration '%s' was (%ldms)",
2014-04-12 17:10:43 +02:00
message, cTimeMs::Now() - durationStart);
}
void LogDuration::show(const char* label)
{
2014-05-09 01:05:21 +02:00
tell(logLevel, "elapsed '%s' at '%s' was (%ldms)",
2014-04-12 17:10:43 +02:00
message, label, cTimeMs::Now() - durationStart);
}
#endif
//***************************************************************************
// Get Unique ID
//***************************************************************************
#ifdef USEUUID
const char* getUniqueId()
{
static char uuid[sizeUuid+TB] = "";
uuid_t id;
uuid_generate(id);
uuid_unparse_upper(id, uuid);
return uuid;
}
#endif // USEUUID
//***************************************************************************
// Create MD5
//***************************************************************************
#ifdef USEMD5
int createMd5(const char* buf, md5* md5)
{
MD5_CTX c;
unsigned char out[MD5_DIGEST_LENGTH];
MD5_Init(&c);
MD5_Update(&c, buf, strlen(buf));
MD5_Final(out, &c);
for (int n = 0; n < MD5_DIGEST_LENGTH; n++)
sprintf(md5+2*n, "%02x", out[n]);
md5[sizeMd5] = 0;
return done;
}
int createMd5OfFile(const char* path, const char* name, md5* md5)
{
FILE* f;
char buffer[1000];
int nread = 0;
MD5_CTX c;
unsigned char out[MD5_DIGEST_LENGTH];
char* file = 0;
asprintf(&file, "%s/%s", path, name);
if (!(f = fopen(file, "r")))
{
2014-05-09 01:05:21 +02:00
tell(0, "Fatal: Can't access '%s'; %s", file, strerror(errno));
2014-04-12 17:10:43 +02:00
free(file);
return fail;
}
free(file);
MD5_Init(&c);
while ((nread = fread(buffer, 1, 1000, f)) > 0)
MD5_Update(&c, buffer, nread);
fclose(f);
MD5_Final(out, &c);
for (int n = 0; n < MD5_DIGEST_LENGTH; n++)
sprintf(md5+2*n, "%02x", out[n]);
md5[sizeMd5] = 0;
return success;
}
#endif // USEMD5
2014-05-09 01:05:21 +02:00
//***************************************************************************
// Url Unescape
//***************************************************************************
/*
* The buffer pointed to by @dst must be at least strlen(@src) bytes.
* Decoding stops at the first character from @src that decodes to null.
* Path normalization will remove redundant slashes and slash+dot sequences,
* as well as removing path components when slash+dot+dot is found. It will
* keep the root slash (if one was present) and will stop normalization
* at the first questionmark found (so query parameters won't be normalized).
*
* @param dst destination buffer
* @param src source buffer
* @param normalize perform path normalization if nonzero
* @return number of valid characters in @dst
*/
int urlUnescape(char* dst, const char* src, int normalize)
{
// CURL* curl;
// int resultSize;
// if (curl_global_init(CURL_GLOBAL_ALL) != 0)
// {
// tell(0, "Error, something went wrong with curl_global_init()");
// return fail;
// }
// curl = curl_easy_init();
// if (!curl)
// {
// tell(0, "Error, unable to get handle from curl_easy_init()");
// return fail;
// }
// dst = curl_easy_unescape(curl, src, strlen(src), &resultSize);
// tell(0, " [%.40s]", src);
// tell(0, "res size %d [%.40s]", resultSize, dst);
// return resultSize;
char* org_dst = dst;
int slash_dot_dot = 0;
char ch, a, b;
a = 0;
do {
ch = *src++;
if (ch == '%' && isxdigit(a = src[0]) && isxdigit(b = src[1]))
{
if (a < 'A')
a -= '0';
else if
(a < 'a') a -= 'A' - 10;
else
a -= 'a' - 10;
if (b < 'A')
b -= '0';
else if (b < 'a')
b -= 'A' - 10;
else
b -= 'a' - 10;
ch = 16 * a + b;
src += 2;
}
if (normalize)
{
switch (ch)
{
case '/': // compress consecutive slashes and remove slash-dot
if (slash_dot_dot < 3)
{
dst -= slash_dot_dot;
slash_dot_dot = 1;
break;
}
// fall-through
case '?': // at start of query, stop normalizing
if (ch == '?')
normalize = 0;
// fall-through
case '\0': // remove trailing slash-dot-(dot)
if (slash_dot_dot > 1)
{
dst -= slash_dot_dot;
// remove parent directory if it was two dots
if (slash_dot_dot == 3)
while (dst > org_dst && *--dst != '/')
; // empty body
slash_dot_dot = (ch == '/') ? 1 : 0;
// keep the root slash if any
if (!slash_dot_dot && dst == org_dst && *dst == '/')
++dst;
}
break;
case '.':
if (slash_dot_dot == 1 || slash_dot_dot == 2)
{
++slash_dot_dot;
break;
}
// fall-through
default:
slash_dot_dot = 0;
}
}
*dst++ = ch;
} while(ch);
return (dst - org_dst) - 1;
}