mirror of
https://projects.vdr-developer.org/git/vdr-plugin-scraper2vdr.git
synced 2023-10-19 17:58:31 +02:00
1205 lines
27 KiB
C
1205 lines
27 KiB
C
/*
|
||
* common.c:
|
||
*
|
||
* See the README file for copyright information and how to reach the author.
|
||
*
|
||
*/
|
||
|
||
#include <sys/stat.h>
|
||
#include <sys/time.h>
|
||
|
||
#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>
|
||
#include <errno.h>
|
||
|
||
#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
|
||
snprintf(t, sizeBuffer, LOG_PREFIX);
|
||
#endif
|
||
|
||
vsnprintf(t+strlen(t), sizeBuffer-strlen(t), format, ap);
|
||
|
||
if (EPG2VDRConfig.logstdout)
|
||
{
|
||
char buf[50+TB];
|
||
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);
|
||
|
||
printf("%s %s\n", buf, t);
|
||
|
||
}
|
||
else
|
||
syslog(LOG_ERR, "%s", t);
|
||
|
||
va_end(ap);
|
||
}
|
||
|
||
//***************************************************************************
|
||
// 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;
|
||
}
|
||
|
||
//***************************************************************************
|
||
// 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);
|
||
}
|
||
|
||
//***************************************************************************
|
||
// 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;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
//***************************************************************************
|
||
// 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;
|
||
}
|
||
|
||
//***************************************************************************
|
||
// TOOLS
|
||
//***************************************************************************
|
||
|
||
int isEmpty(const char* str)
|
||
{
|
||
return !str || !*str;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
//***************************************************************************
|
||
//
|
||
//***************************************************************************
|
||
|
||
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);
|
||
|
||
tell(0, "Error: Detecting state for '%s' failed, error was '%s'", path, strerror(errno));
|
||
|
||
return false;
|
||
}
|
||
|
||
const char* suffixOf(const char* path)
|
||
{
|
||
const char* p;
|
||
|
||
if (path && (p = strrchr(path, '.')))
|
||
return p+1;
|
||
|
||
return "";
|
||
}
|
||
|
||
int fileSize(const char* path)
|
||
{
|
||
struct stat sb;
|
||
|
||
if (lstat(path, &sb) == 0)
|
||
return sb.st_size;
|
||
|
||
tell(0, "Error: Detecting state for '%s' failed, error was '%s'", path, strerror(errno));
|
||
|
||
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;
|
||
}
|
||
|
||
|
||
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)
|
||
{
|
||
tell(0, "Failed to create symlink '%s', error was '%s'", link, strerror(errno));
|
||
return fail;
|
||
}
|
||
}
|
||
|
||
return success;
|
||
}
|
||
|
||
//***************************************************************************
|
||
// Remove File
|
||
//***************************************************************************
|
||
|
||
int removeFile(const char* filename)
|
||
{
|
||
int lnk = isLink(filename);
|
||
|
||
if (unlink(filename) != 0)
|
||
{
|
||
tell(0, "Can't remove file '%s', '%s'", filename, strerror(errno));
|
||
|
||
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)
|
||
{
|
||
tell(0, "Can't create directory '%s'", strerror(errno));
|
||
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;
|
||
}
|
||
|
||
//***************************************************************************
|
||
// 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;
|
||
}
|
||
|
||
//*************************************************************************
|
||
// 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;
|
||
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;
|
||
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;
|
||
default: tell(0, "Error: Couldn't zip/unzip data for unknown reason (%6d)%s!\n", errorCode, op); return;
|
||
}
|
||
}
|
||
|
||
//*************************************************************************
|
||
// 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)
|
||
{
|
||
tell(0, "Error: Open '%s' failed - %s", file, strerror(errno));
|
||
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()
|
||
{
|
||
tell(logLevel, "duration '%s' was (%ldms)",
|
||
message, cTimeMs::Now() - durationStart);
|
||
}
|
||
|
||
void LogDuration::show(const char* label)
|
||
{
|
||
tell(logLevel, "elapsed '%s' at '%s' was (%ldms)",
|
||
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")))
|
||
{
|
||
tell(0, "Fatal: Can't access '%s'; %s", file, strerror(errno));
|
||
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
|
||
|
||
//***************************************************************************
|
||
// 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;
|
||
}
|