mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 06:05:58 -06:00
881 lines
19 KiB
C++
881 lines
19 KiB
C++
#include "Web.h"
|
|
|
|
NAMESPACE_UPP
|
|
|
|
bool IsSameTextFile(const char *p, const char *q)
|
|
{
|
|
for(;;)
|
|
{
|
|
if(*p == 0)
|
|
{
|
|
while(*q && (byte)*q <= ' ')
|
|
q++;
|
|
return *q == 0;
|
|
}
|
|
if(*q == 0)
|
|
{
|
|
while(*p && (byte)*p <= ' ')
|
|
p++;
|
|
return *p == 0;
|
|
}
|
|
bool pcr = false;
|
|
if(*p == '\r' || *p == '\n')
|
|
{
|
|
if(p[1] == (*p == '\r' ? '\n' : '\r'))
|
|
p++;
|
|
p++;
|
|
pcr = true;
|
|
}
|
|
bool qcr = false;
|
|
if(*q == '\r' || *q == '\n')
|
|
{
|
|
if(q[1] == (*q == '\r' ? '\n' : '\r'))
|
|
q++;
|
|
q++;
|
|
qcr = true;
|
|
}
|
|
if(pcr || qcr)
|
|
{
|
|
if(pcr != qcr)
|
|
return false;
|
|
}
|
|
else if(*p++ != *q++)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
String StringSample(const char *s, int limit)
|
|
{
|
|
const char *e = (const char *)memchr(s, 0, limit + 1);
|
|
if(e)
|
|
return String(s, e);
|
|
return String(s, limit) + "...";
|
|
}
|
|
|
|
static String ToQuotedHtml(String s)
|
|
{
|
|
String out;
|
|
for(const char *p = s.Begin(), *e = s.End(); p < e; p++)
|
|
switch(*p)
|
|
{
|
|
case 31: out.Cat(" "); break;
|
|
case '<': out.Cat("<"); break;
|
|
case '>': out.Cat(">"); break;
|
|
case '&': out.Cat("&"); break;
|
|
case '\"': out.Cat("""); break;
|
|
case '\r': break;
|
|
case '\t': out.Cat("	"); break;
|
|
case '\n': out.Cat(" "); break;
|
|
default: out.Cat(*p); break;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
String FormatIP(dword _ip)
|
|
{
|
|
byte ip[4];
|
|
Poke32be(ip, _ip);
|
|
return Format("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
|
|
}
|
|
|
|
static const char hex_digits[] = "0123456789ABCDEF";
|
|
|
|
String UrlEncode(String s)
|
|
{
|
|
const char *p = s, *e = s.End();
|
|
String out;
|
|
for(; p < e; p++)
|
|
{
|
|
const char *b = p;
|
|
while(p < e && (byte)*p > ' ' && (byte)*p < 127
|
|
&& (IsAlNum(*p) || *p == ',' || *p == '.' || *p == '-'))
|
|
p++;
|
|
if(p > b)
|
|
out.Cat(b, int(p - b));
|
|
if(p >= e)
|
|
break;
|
|
if(*p == ' ')
|
|
out << '+';
|
|
else
|
|
out << '%' << hex_digits[(*p >> 4) & 15] << hex_digits[*p & 15];
|
|
}
|
|
return out;
|
|
}
|
|
|
|
String UrlEncode(String s, const char *specials)
|
|
{
|
|
int l = (int)strlen(specials);
|
|
const char *p = s, *e = s.End();
|
|
String out;
|
|
for(; p < e; p++)
|
|
{
|
|
const char *b = p;
|
|
while(p < e && (byte)*p > ' ' && (byte)*p < 127 && memchr(specials, *p, l) == 0)
|
|
p++;
|
|
if(p > b)
|
|
out.Cat(b, int(p - b));
|
|
if(p >= e)
|
|
break;
|
|
if(*p == ' ')
|
|
out << '+';
|
|
else
|
|
out << '%' << hex_digits[(*p >> 4) & 15] << hex_digits[*p & 15];
|
|
}
|
|
return out;
|
|
}
|
|
|
|
String UrlDecode(String s)
|
|
{
|
|
const char *p = s, *e = p + s.GetLength();
|
|
String out;
|
|
for(; p < e; p++)
|
|
if(*p == '+')
|
|
out.Cat(' ');
|
|
else if(*p == '%' && isxdigit(p[1]))
|
|
{
|
|
byte d1 = ctoi(*++p);
|
|
byte d2 = ctoi(p[1]);
|
|
if(d2 < 16)
|
|
{
|
|
p++;
|
|
d1 = (d1 << 4) | d2;
|
|
}
|
|
out.Cat(d1);
|
|
}
|
|
else
|
|
out.Cat(*p);
|
|
return out;
|
|
}
|
|
|
|
String GetRandomIdent(int length)
|
|
{
|
|
StringBuffer ident(length);
|
|
char *p = ident;
|
|
for(int i = 0; i < length; i++)
|
|
*p++ = 'A' + rand() % ('Z' - 'A' + 1);
|
|
return ident;
|
|
}
|
|
|
|
static unsigned AddCRC(unsigned old, byte next, unsigned crc)
|
|
{
|
|
for(int i = 0; i < 8; i++)
|
|
{
|
|
old = (old << 1) ^ (old & 0x80000000u ? crc : 0) ^ (next & 1);
|
|
next >>= 1;
|
|
}
|
|
return old;
|
|
}
|
|
|
|
enum { CRC = 0x84030625 };
|
|
|
|
String OtpEncode(String password, String otp_seed)
|
|
{
|
|
if(IsNull(password))
|
|
return Null;
|
|
unsigned crc_sum[4];
|
|
memset(crc_sum, 0, sizeof(crc_sum));
|
|
for(int j = 0; j < otp_seed.GetLength(); j++)
|
|
{
|
|
unsigned sum = crc_sum[j & 3];
|
|
sum = AddCRC(sum, otp_seed[j], CRC);
|
|
for(int i = 0; i < password.GetLength(); i++)
|
|
sum = AddCRC(sum, password[i], CRC);
|
|
crc_sum[j & 3] = sum;
|
|
}
|
|
char h[100];
|
|
sprintf(h, "%08X%08X%08X%08X", crc_sum[0], crc_sum[1], crc_sum[2], crc_sum[3]);
|
|
return h;
|
|
}
|
|
|
|
String EncryptString(String password, String otp_key)
|
|
{
|
|
unsigned crc = 0;
|
|
int i;
|
|
for(i = 0; i < otp_key.GetLength(); i++)
|
|
crc = AddCRC(crc, otp_key[i], CRC);
|
|
int pl = password.GetLength();
|
|
StringBuffer out(pl);
|
|
char *p = out;
|
|
for(i = 0; i < pl; i++)
|
|
{
|
|
*p++ = password[i] ^ crc;
|
|
crc = AddCRC(crc, otp_key[(int)(crc % otp_key.GetLength())], CRC);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
String BinHexEncode(const char *s, const char *e)
|
|
{
|
|
static const char bh[] = "0123456789ABCDEF";
|
|
int l = int(e - s);
|
|
StringBuffer out(2 * l);
|
|
char *p = out;
|
|
for(; s < e; s++) {
|
|
*p++ = bh[(*s >> 4) & 0xF];
|
|
*p++ = bh[*s & 0xF];
|
|
}
|
|
return out;
|
|
}
|
|
|
|
String BinHexDecode(const char *p, const char *e)
|
|
{
|
|
StringBuffer out;
|
|
if((e - p) & 1)
|
|
e--;
|
|
for(; p < e; p++)
|
|
if(IsXDigit(*p) && IsXDigit(p[1])) {
|
|
out.Cat(ctoi(p[0]) * 16 + ctoi(p[1]));
|
|
p++;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
String Base64Encode(const char *b, const char *e)
|
|
{
|
|
static const char encoder[] =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
"abcdefghijklmnopqrstuvwxyz"
|
|
"0123456789+/";
|
|
if(b == e)
|
|
return Null;
|
|
int out = (int(e - b) + 2) / 3 * 4;
|
|
int rem = int(e - b) % 3;
|
|
e -= rem;
|
|
StringBuffer s(out);
|
|
char *p = s;
|
|
while(b < e)
|
|
{
|
|
p[0] = encoder[(b[0] >> 2) & 0x3F];
|
|
p[1] = encoder[((b[0] << 4) & 0x30) | ((b[1] >> 4) & 0x0F)];
|
|
p[2] = encoder[((b[1] << 2) & 0x3C) | ((b[2] >> 6) & 0x03)];
|
|
p[3] = encoder[b[2] & 0x3F];
|
|
b += 3;
|
|
p += 4;
|
|
}
|
|
if(rem == 1)
|
|
{
|
|
p[0] = encoder[(b[0] >> 2) & 0x3F];
|
|
p[1] = encoder[(b[0] << 4) & 0x30];
|
|
p[2] = p[3] = '=';
|
|
}
|
|
else if(rem == 2)
|
|
{
|
|
p[0] = encoder[(b[0] >> 2) & 0x3F];
|
|
p[1] = encoder[((b[0] << 4) & 0x30) | ((b[1] >> 4) & 0x0F)];
|
|
p[2] = encoder[(b[1] << 2) & 0x3C];
|
|
p[3] = '=';
|
|
}
|
|
return s;
|
|
}
|
|
|
|
String Base64Decode(const char *b, const char *e)
|
|
{
|
|
static byte dec64[] =
|
|
{
|
|
/* 0x */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
/* 1x */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
/* 2x */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x3F,
|
|
/* 3x */0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
/* 4x */0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
|
|
/* 5x */0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
/* 6x */0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
|
/* 7x */0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
/* 8x */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
/* 9x */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
/* Ax */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
/* Bx */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
/* Cx */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
/* Dx */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
/* Ex */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
/* Fx */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
};
|
|
StringBuffer out;
|
|
byte c[4];
|
|
int pos = 0;
|
|
for(; b < e; b++)
|
|
if((byte)*b > ' ') {
|
|
byte ch = dec64[(byte)*b];
|
|
if(ch & 0xC0)
|
|
break;
|
|
c[pos++] = ch;
|
|
if(pos == 4) {
|
|
out.Cat((c[0] << 2) | (c[1] >> 4));
|
|
out.Cat((c[1] << 4) | (c[2] >> 2));
|
|
out.Cat((c[2] << 6) | (c[3] >> 0));
|
|
pos = 0;
|
|
}
|
|
}
|
|
if(pos >= 2) {
|
|
out.Cat((c[0] << 2) | (c[1] >> 4));
|
|
if(pos >= 3) {
|
|
out.Cat((c[1] << 4) | (c[2] >> 2));
|
|
if(pos >= 4)
|
|
out.Cat((c[2] << 6) | (c[3] >> 0));
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
String ASCII85Encode(const byte *p, int length)
|
|
{
|
|
StringBuffer out;
|
|
out.Reserve((length >> 2) * 5 + (length & 3) + 1);
|
|
while(length >= 4)
|
|
{
|
|
dword chunk = (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
|
|
if(chunk == 0)
|
|
out.Cat('z');
|
|
else {
|
|
out.Cat(33 + chunk / (85 * 85 * 85 * 85));
|
|
out.Cat(33 + (chunk / (85 * 85 * 85)) % 85);
|
|
out.Cat(33 + (chunk / (85 * 85)) % 85);
|
|
out.Cat(33 + (chunk / 85) % 85);
|
|
out.Cat(33 + chunk % 85);
|
|
}
|
|
p += 4;
|
|
length -= 4;
|
|
}
|
|
if(length)
|
|
{
|
|
dword chunk = *p++ << 24;
|
|
if(length > 1) chunk += *p++ << 16;
|
|
if(length > 2) chunk += *p++ << 8;
|
|
out.Cat(33 + chunk / (85 * 85 * 85 * 85));
|
|
out.Cat(33 + (chunk / (85 * 85 * 85)) % 85);
|
|
if(length > 1)
|
|
out.Cat(33 + (chunk / (85 * 85)) % 85);
|
|
if(length > 2)
|
|
out.Cat(33 + (chunk / 85) % 85);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
String ASCII85Decode(const byte *p, int length)
|
|
{
|
|
byte buffer[5];
|
|
int pos = 0;
|
|
String out;
|
|
for(const byte *e = p + length; p < e; p++)
|
|
if(*p >= 33 && *p < 33 + 85)
|
|
{
|
|
buffer[pos++] = *p - 33;
|
|
if(pos >= 5)
|
|
{
|
|
unsigned v = buffer[4] + 85 * (buffer[3] + 85 * (buffer[2] + 85 * (buffer[1] + 85 * buffer[0])));
|
|
out.Cat((byte)(v >> 24));
|
|
out.Cat((byte)(v >> 16));
|
|
out.Cat((byte)(v >> 8));
|
|
out.Cat((byte)v);
|
|
pos = 0;
|
|
}
|
|
}
|
|
else if(*p == 'z')
|
|
{
|
|
out.Cat("\0\0\0", 4);
|
|
pos = 0;
|
|
}
|
|
switch(pos)
|
|
{
|
|
case 2:
|
|
{
|
|
unsigned v = 84 + 84 * 85 + 84 * 85 * 85 + 85 * 85 * 85 * (buffer[1] + 85 * buffer[0]);
|
|
out.Cat((byte)(v >> 24));
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
{
|
|
unsigned v = 84 + 84 * 85 + 85 * 85 * (buffer[2] + 85 * (buffer[1] + 85 * buffer[0]));
|
|
out.Cat((byte)(v >> 24));
|
|
out.Cat((byte)(v >> 16));
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
{
|
|
unsigned v = 84 + 85 * (buffer[3] + 85 * (buffer[2] + 85 * (buffer[1] + 85 * buffer[0])));
|
|
out.Cat((byte)(v >> 24));
|
|
out.Cat((byte)(v >> 16));
|
|
out.Cat((byte)(v >> 8));
|
|
}
|
|
break;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
static const dword *GetCRCTable()
|
|
{
|
|
enum { CTL = 0xDAA97221 };
|
|
static dword table[256];
|
|
if(table[1] == 0)
|
|
{
|
|
for(int i = 1; i < __countof(table); i++)
|
|
{
|
|
dword v = i << 24;
|
|
for(int t = 0; t < 8; t++)
|
|
v = (v << 1) ^ (v & (1 << 31) ? CTL : 0);
|
|
table[i] = v;
|
|
}
|
|
}
|
|
return table;
|
|
}
|
|
|
|
dword AddCRC(dword crc, const byte *data, int count)
|
|
{
|
|
const dword *t = GetCRCTable();
|
|
for(const byte *e = data + count; data < e; data++)
|
|
crc = ((crc << 8) | (*data & 0xFF)) ^ t[(crc >> 24) & 0xFF];
|
|
return crc;
|
|
}
|
|
|
|
static void GetLineIndex(String file, HashBase& hash, Vector<String>& lines)
|
|
{
|
|
const char *p = file;
|
|
while(*p)
|
|
{
|
|
while(*p && *p != '\n' && (byte)*p <= ' ')
|
|
p++;
|
|
const char *b = p;
|
|
while(*p && *p++ != '\n')
|
|
;
|
|
const char *e = p;
|
|
while(e > b && (byte)e[-1] <= ' ')
|
|
e--;
|
|
String s(b, e);
|
|
hash.Add(GetHashValue(s));
|
|
lines.Add(s);
|
|
}
|
|
}
|
|
|
|
int LocateLine(String old_file, int old_line, String new_file)
|
|
{
|
|
HashBase old_hash, new_hash;
|
|
Vector<String> old_lines, new_lines;
|
|
GetLineIndex(old_file, old_hash, old_lines);
|
|
GetLineIndex(new_file, new_hash, new_lines);
|
|
if(old_line <= 0)
|
|
return 0;
|
|
if(old_line >= old_lines.GetCount())
|
|
return new_lines.GetCount();
|
|
String line = old_lines[old_line];
|
|
//int hash = old_hash[old_line]; Mirek: unused
|
|
//int fore_count = old_lines.GetCount() - old_line - 1;
|
|
int best_match = 0, best_value = 0;
|
|
for(int r = 0; r < 10 && !best_value; r++)
|
|
{
|
|
int src = (r & 1 ? old_line + (r >> 1) + 1 : old_line - (r >> 1));
|
|
if(src < 0 || src >= old_lines.GetCount())
|
|
continue;
|
|
dword hash = old_hash[src];
|
|
for(int i = new_hash.Find(hash); i >= 0; i = new_hash.FindNext(i))
|
|
if(new_lines[i] == old_lines[src])
|
|
{
|
|
int max_back = min(i, src);
|
|
int max_fore = min(new_lines.GetCount() - i, old_lines.GetCount() - src) - 1;
|
|
if(max_back + max_fore <= best_value)
|
|
continue;
|
|
int back = 1;
|
|
while(back <= max_back && new_hash[i - back] == old_hash[src - back]
|
|
&& new_lines[i - back] == old_lines[src - back])
|
|
back++;
|
|
int fore = 1;
|
|
while(fore < max_fore && new_hash[i + fore] == old_hash[src + fore]
|
|
&& new_lines[i + fore] == old_lines[src + fore])
|
|
fore++;
|
|
if(back + fore > best_value)
|
|
{
|
|
best_value = back + fore;
|
|
best_match = minmax(i, 0, new_lines.GetCount());
|
|
}
|
|
}
|
|
}
|
|
return best_match;
|
|
}
|
|
|
|
void AppVersion(const char *ver)
|
|
{
|
|
#ifdef PLATFORM_WIN32
|
|
String out = "set RAW_VERSION=";
|
|
out.Cat(ver);
|
|
out.Cat("\r\n"
|
|
"set APP_VERSION=");
|
|
for(; *ver; ver++)
|
|
out.Cat(IsAlNum(*ver) ? *ver : '_');
|
|
out.Cat("\r\n");
|
|
SaveFile("ver.bat", out);
|
|
#else
|
|
fputs(ver, stdout);
|
|
#endif
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// HttpQuery::
|
|
|
|
RefPtr<HttpQuery::Data> HttpQuery::Empty()
|
|
{
|
|
static RefPtr<Data> emp = new Data;
|
|
return emp;
|
|
}
|
|
|
|
void HttpQuery::Serialize(Stream& stream)
|
|
{
|
|
int version = StreamHeading(stream, 1, 1, 1, "HttpQuery");
|
|
if(version >= 1)
|
|
{
|
|
if(stream.IsLoading())
|
|
Clear();
|
|
int count = GetCount();
|
|
stream / count;
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
String key, value;
|
|
if(stream.IsStoring())
|
|
{
|
|
key = data -> map.GetKey(i);
|
|
value = data -> map[i];
|
|
}
|
|
stream % key % value;
|
|
if(stream.IsLoading())
|
|
Set(key, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
HttpQuery& HttpQuery::SetURL(String url)
|
|
{
|
|
const char *p = url;
|
|
while(*p && *p != '\1' && *p != '?')
|
|
p++;
|
|
p = (*p == '?' ? p + 1 : ~url);
|
|
|
|
// const char *e = url.End(); Mirek:unused
|
|
while(*p)
|
|
{
|
|
const char *last = p;
|
|
while(*p && *p != '=' && *p != '&')
|
|
p++;
|
|
String key = ToUpper(UrlDecode(String(last, p)));
|
|
if(*p == '=')
|
|
p++;
|
|
last = p;
|
|
while(*p && *p != '&')
|
|
p++;
|
|
Set(key, UrlDecode(String(last, p)));
|
|
if(*p)
|
|
p++;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
String HttpQuery::GetHidden() const
|
|
{
|
|
String out;
|
|
for(int i = 0; i < GetCount(); i++)
|
|
if(!IsInternal(i))
|
|
{
|
|
out << "<INPUT TYPE=HIDDEN NAME=\"" << ToHtml(GetKey(i))
|
|
<< "\" VALUE=\"" << ToQuotedHtml(GetValue(i)) << "\">\n";
|
|
}
|
|
return out;
|
|
}
|
|
|
|
String HttpQuery::GetQuery(bool empty) const
|
|
{
|
|
String result;
|
|
for(int i = 0, n = GetCount(); i < n; i++)
|
|
if(!IsInternal(i))
|
|
{
|
|
String key = GetKey(i), value = GetValue(i);
|
|
if(empty || !value.IsEmpty())
|
|
{
|
|
if(!result.IsEmpty()) result.Cat('&');
|
|
result.Cat(UrlEncode(key));
|
|
result.Cat('=');
|
|
result.Cat(UrlEncode(value));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
String HttpQuery::GetQuery(HttpQuery patch, bool empty) const
|
|
{
|
|
String result;
|
|
int i, n;
|
|
for(i = 0, n = GetCount(); i < n; i++)
|
|
if(!IsInternal(i))
|
|
{
|
|
String key = GetKey(i), value = GetValue(i);
|
|
if(patch.Find(key) < 0)
|
|
{
|
|
if(empty || !value.IsEmpty())
|
|
{
|
|
if(!result.IsEmpty()) result.Cat('&');
|
|
result.Cat(UrlEncode(key));
|
|
result.Cat('=');
|
|
result.Cat(UrlEncode(value));
|
|
}
|
|
}
|
|
}
|
|
for(i = 0, n = patch.GetCount(); i < n; i++)
|
|
if(!patch.IsInternal(i))
|
|
{
|
|
String key = patch.GetKey(i), value = patch.GetValue(i);
|
|
if(empty || !value.IsEmpty())
|
|
{
|
|
if(!result.IsEmpty()) result.Cat('&');
|
|
result.Cat(UrlEncode(key));
|
|
result.Cat('=');
|
|
result.Cat(UrlEncode(value));
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool HttpQuery::IsEmpty(String key) const
|
|
{
|
|
int f = Find(key);
|
|
return f < 0 || data -> map[f].IsEmpty();
|
|
}
|
|
|
|
bool HttpQuery::IsInternal(int i) const
|
|
{
|
|
String key = GetKey(i);
|
|
return key[0] == '$' && key[1] == '$';
|
|
}
|
|
|
|
void HttpQuery::Get(String key, Ref result) const
|
|
{
|
|
switch(result.GetType())
|
|
{
|
|
case BOOL_V: result.SetValue(GetBool(key)); break;
|
|
case INT_V: RefInt(result) = GetInt(key); break;
|
|
case DOUBLE_V: RefDouble(result) = GetDouble(key); break;
|
|
case STRING_V: RefString(result) = GetString(key); break;
|
|
case DATE_V: RefDate(result) = GetDate(key); break;
|
|
case TIME_V: RefTime(result) = GetTime(key); break;
|
|
default: NEVER();
|
|
}
|
|
}
|
|
|
|
bool HttpQuery::GetBool(String key) const
|
|
{
|
|
int i = Find(key);
|
|
return i >= 0 && data -> map[i] != "0";
|
|
}
|
|
|
|
int HttpQuery::GetInt(String key) const
|
|
{
|
|
int i = Find(key);
|
|
return i < 0 || data->map[i].IsEmpty() ? (int)Null : atoi(data->map[i]);
|
|
}
|
|
|
|
double HttpQuery::GetDouble(String key) const
|
|
{
|
|
int i = Find(key);
|
|
return i < 0 || data->map[i].IsEmpty() ? (double)Null : atof(data->map[i]);
|
|
}
|
|
|
|
String HttpQuery::GetString(String key) const
|
|
{
|
|
int i = Find(key);
|
|
return i >= 0 ? data -> map[i] : String::GetVoid();
|
|
}
|
|
|
|
Date HttpQuery::GetDate(String key) const
|
|
{
|
|
int i = Find(key);
|
|
return i >= 0 ? Date(StdConvertDate().Scan(data -> map[i])) : Date(Null);
|
|
}
|
|
|
|
Time HttpQuery::GetTime(String key) const
|
|
{
|
|
int i = Find(key);
|
|
return i >= 0 ? Time(StdConvertTime().Scan(data -> map[i])) : Time(Null);
|
|
}
|
|
|
|
Color HttpQuery::GetColor(String key) const
|
|
{
|
|
String s = GetString(key);
|
|
if(s.GetLength() >= 6)
|
|
{
|
|
int digit[6];
|
|
int i = 0;
|
|
while(i < 6 && (digit[i] = ctoi(s[i])) < 16)
|
|
i++;
|
|
if(i >= 6)
|
|
return Color(digit[0] * 16 + digit[1], digit[2] * 16 + digit[3], digit[4] * 16 + digit[5]);
|
|
}
|
|
return Null;
|
|
}
|
|
|
|
HttpQuery& HttpQuery::SetColor(String key, Color c)
|
|
{
|
|
if(IsNull(c))
|
|
SetString(key, Null);
|
|
else
|
|
SetString(key, NFormat("%02x%02x%02x", c.GetR(), c.GetG(), c.GetB()));
|
|
return *this;
|
|
}
|
|
|
|
bool HttpQuery::GetBool(String key, bool dflt) const
|
|
{
|
|
int i = Find(key);
|
|
return i >= 0 ? data -> map[i] != "0" : dflt;
|
|
}
|
|
|
|
int HttpQuery::GetInt(String key, int min, int max, int dflt) const
|
|
{
|
|
int v = GetInt(key);
|
|
return !IsNull(v) ? minmax(v, min, max) : dflt;
|
|
}
|
|
|
|
double HttpQuery::GetDouble(String key, double min, double max, double dflt) const
|
|
{
|
|
double v = GetDouble(key);
|
|
return !IsNull(v) ? minmax(v, min, max) : dflt;
|
|
}
|
|
|
|
String HttpQuery::GetString(String key, String dflt) const
|
|
{
|
|
int i = Find(key);
|
|
return i >= 0 && !data -> map[i].IsEmpty() ? data -> map[i] : dflt;
|
|
}
|
|
|
|
HttpQuery& HttpQuery::SetBool(String key, bool b)
|
|
{
|
|
return Set(key, b ? "1" : "");
|
|
}
|
|
|
|
HttpQuery& HttpQuery::SetInt(String key, int i)
|
|
{
|
|
return Set(key, IntStr(i));
|
|
}
|
|
|
|
HttpQuery& HttpQuery::SetDouble(String key, double f)
|
|
{
|
|
return Set(key, DblStr(f));
|
|
}
|
|
|
|
HttpQuery& HttpQuery::Set(String key, String s)
|
|
{
|
|
int i = Find(key);
|
|
if(i < 0 || data -> map[i] != s)
|
|
{
|
|
Clone();
|
|
if(i < 0)
|
|
data -> map.Add(key, s);
|
|
else
|
|
data -> map[i] = s;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
HttpQuery& HttpQuery::SetDate(String key, Date d)
|
|
{
|
|
return Set(key, Format(d));
|
|
}
|
|
|
|
HttpQuery& HttpQuery::SetTime(String key, Time t)
|
|
{
|
|
return Set(key, Format(t));
|
|
}
|
|
|
|
HttpQuery& HttpQuery::SetValue(String key, Value v)
|
|
{
|
|
return Set(key, StdFormat(v));
|
|
}
|
|
|
|
HttpQuery& HttpQuery::Set(HttpQuery query)
|
|
{
|
|
if(int n = query.GetCount())
|
|
{
|
|
Clone();
|
|
RefCon<Data> s = query.data;
|
|
for(int i = 0; i < n; i++)
|
|
Set(s -> map.GetKey(i), s -> map[i]);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
HttpQuery& HttpQuery::Remove(String key)
|
|
{
|
|
int i = Find(key);
|
|
if(i < 0)
|
|
return *this;
|
|
if(data -> map.GetCount() == 1)
|
|
Clear();
|
|
else
|
|
{
|
|
Clone();
|
|
data -> map.Remove(i);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
HttpQuery& HttpQuery::Remove(const Vector<String>& keys)
|
|
{
|
|
if(!data)
|
|
return *this;
|
|
Index<int> todo;
|
|
for(int i = 0; i < keys.GetCount(); i++)
|
|
{
|
|
int f = data -> map.Find(keys[i]);
|
|
if(f >= 0)
|
|
todo.FindAdd(f);
|
|
}
|
|
if(todo.IsEmpty())
|
|
return *this;
|
|
Clone();
|
|
Vector<int> todo_us = todo.PickKeys();
|
|
Sort(todo_us);
|
|
while(!todo_us.IsEmpty())
|
|
data -> map.Remove(todo_us.Pop());
|
|
return *this;
|
|
}
|
|
|
|
HttpQuery& HttpQuery::Remove(const Vector<Id>& keys)
|
|
{
|
|
if(!data)
|
|
return *this;
|
|
Index<int> todo;
|
|
for(int i = 0; i < keys.GetCount(); i++)
|
|
{
|
|
int f = data -> map.Find(~keys[i]);
|
|
if(f >= 0)
|
|
todo.FindAdd(f);
|
|
}
|
|
if(todo.IsEmpty())
|
|
return *this;
|
|
Clone();
|
|
Vector<int> todo_us = todo.PickKeys();
|
|
Sort(todo_us);
|
|
while(!todo_us.IsEmpty())
|
|
data -> map.Remove(todo_us.Pop());
|
|
return *this;
|
|
}
|
|
|
|
String HttpQuery::ToString() const
|
|
{
|
|
String out;
|
|
out << "HttpQuery (" << GetCount() << " items)\n";
|
|
for(int i = 0; i < GetCount(); i++)
|
|
{
|
|
out << "[" << i;
|
|
if(IsInternal(i))
|
|
out << ":internal";
|
|
out << "]: <" << GetKey(i) << "> = <";
|
|
String value = GetValue(i);
|
|
if(value.GetLength() <= 1000)
|
|
out << value;
|
|
else
|
|
{
|
|
out.Cat(value, 1000);
|
|
out << " ... (length = " << value.GetLength() << ")";
|
|
}
|
|
out << ">\n";
|
|
}
|
|
return out;
|
|
}
|
|
|
|
END_UPP_NAMESPACE
|