From 21305f6f705b68de4c95bef9d048ff64c331e982 Mon Sep 17 00:00:00 2001 From: cxl Date: Sat, 14 Jul 2012 18:14:25 +0000 Subject: [PATCH] *Skylark: fixed cookies in redirect git-svn-id: svn://ultimatepp.org/upp/trunk@5190 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- uppsrc/Skylark/Compile.cpp | 801 ++++++++++++++++++------------------ uppsrc/Skylark/Dispatch.cpp | 4 +- uppsrc/Skylark/Http.cpp | 698 +++++++++++++++---------------- uppsrc/Skylark/Renderer.cpp | 161 ++++---- uppsrc/Skylark/Session.cpp | 329 ++++++++------- 5 files changed, 995 insertions(+), 998 deletions(-) diff --git a/uppsrc/Skylark/Compile.cpp b/uppsrc/Skylark/Compile.cpp index 581ab6db0..cfba2cf4a 100644 --- a/uppsrc/Skylark/Compile.cpp +++ b/uppsrc/Skylark/Compile.cpp @@ -1,402 +1,401 @@ -#include "Skylark.h" - -#define LLOG(x) // DLOG(x) - -namespace Upp { - -Value Raw(const String& s) -{ - RawHtmlText r; - r.text = s; - return RawToValue(r); -} - -VectorMap&, const Renderer *)>& Compiler::functions() -{ - static VectorMap&, const Renderer *)> x; - return x; -} - -void Compiler::Register(const String& id, Value (*fn)(const Vector&, const Renderer *)) -{ - functions().GetAdd(id) = fn; -} - -int Compiler::ForVar(String id, int i) -{ - if(i + 1 < var.GetCount() && forvar[i]) - return i + 1; - p.ThrowError(id + " is not 'for' iterator"); - return 0; -} - -int CountLinkArgs(const Vector& part) -{ - int args = 0; - for(int i = 0; i < part.GetCount(); i++) { - int p = (byte)*part[i]; - if(p >= 0 && p < 30) - args = max(args, p + 1); - } - return args; -} - -One Compiler::Prim() -{ - One result; - DDUMP(p.GetPtr()); - if(p.Char('!')) - result = Create(Prim()); - else - if(p.Char('-')) - result = Create(Prim()); - else - if(p.Char('+')) - result = Prim(); - else - if(p.IsId() || p.IsChar('.') || p.IsChar('@')) { - String id; - if(p.Char('.')) - id = "."; - else - if(p.Char('@')) - id = "@"; - id << p.ReadId(); - int n = var.Find(id); - if(p.Char('(')) { - Value (*f)(const Vector&, const Renderer *) = functions().Get(id, NULL); - if(!f) { - Vector *part = GetUrlViewLinkParts(id); - if(!part) - p.ThrowError("function nor link not found '" + id + "'"); - ExeLink& ln = result.Create(); - ln.part = part; - if(!p.Char(')')) { - do - ln.arg.AddPick(Exp()); - while(p.Char(',')); - p.PassChar(')'); - } - if(CountLinkArgs(*part) > ln.arg.GetCount()) - p.ThrowError("invalid number of link arguments '" + id + "'"); - } - else { - ExeFn& fn = result.Create(); - fn.fn = f; - if(!p.Char(')')) { - do - fn.arg.AddPick(Exp()); - while(p.Char(',')); - p.PassChar(')'); - } - while(p.Char('.')) { - One r; - ExeField& f = r.Create(); - f.value = result; - f.id = p.ReadId(); - result = r; - } - } - return result; - } - if(n < 0) { - Vector *part = GetUrlViewLinkParts(id); - ExeConst& c = result.Create(); - if(!part) { - return result; - } - String l = "\"/"; - for(int i = 0; i < (*part).GetCount(); i++) { - if(i) - l << '/'; - l << UrlEncode((*part)[i]); - } - l << '\"'; - c.value = Raw(l); - } - else - if(p.Char('.')) { - if(p.Id("_first")) - result.Create().var_index = ForVar(id, n); - else - if(p.Id("_last")) - result.Create().var_index = ForVar(id, n); - else - if(p.Id("_index")) - result.Create().var_index = ForVar(id, n); - else - if(p.Id("_key")) - result.Create().var_index = ForVar(id, n); - else { - result.Create().var_index = n; - do { - One r; - ExeField& f = r.Create(); - f.value = result; - f.id = p.ReadId(); - result = r; - } - while(p.Char('.')); - } - } - else - result.Create().var_index = n; - } - else - if(p.Char('{')) { - ExeMap& m = result.Create(); - do { - m.key.AddPick(Exp()); - p.PassChar(':'); - m.value.AddPick(Exp()); - } - while(p.Char(',')); - p.PassChar('}'); - } - else - if(p.Char('[')) { - ExeArray& m = result.Create(); - do { - m.item.AddPick(Exp()); - } - while(p.Char(',')); - p.PassChar(']'); - } - else - if(p.Char('(')) { - result = Exp(); - p.PassChar(')'); - } - else { - ExeConst& c = result.Create(); - if(p.Char2('0', 'x') || p.Char2('0', 'X')) - c.value = (int)p.ReadNumber(16); - else - if(p.Char('0')) - c.value = int(p.IsNumber() ? p.ReadNumber(8) : 0); - else - c.value = p.IsString() ? Value(p.ReadString()) : Value(p.ReadDouble()); - } - return result; -} - -One Compiler::Mul() -{ - One result = Prim(); - for(;;) - if(p.Char('*')) - result = Create(result, Prim()); - else - if(p.Char('/')) - result = Create(result, Prim()); - else - if(p.Char('%')) - result = Create(result, Prim()); - else - return result; -} - -One Compiler::Add() -{ - One result = Mul(); - for(;;) - if(p.Char('+')) - result = Create(result, Mul()); - else - if(p.Char('-')) - result = Create(result, Mul()); - else - return result; -} - -One Compiler::Shift() -{ - One result = Add(); - for(;;) - if(p.Char3('>', '>', '>')) - result = Create(result, Add()); - else - if(p.Char2('>', '>')) - result = Create(result, Add()); - else - if(p.Char2('<', '<')) - result = Create(result, Add()); - else - return result; -} - -One Compiler::Rel() -{ - One result = Shift(); - for(;;) - if(p.Char2('<', '=')) - result = Create(result, Shift()); - else - if(p.Char2('>', '=')) - result = Create(Shift(), result); - else - if(p.Char('<')) - result = Create(result, Shift()); - else - if(p.Char('>')) - result = Create(Shift(), result); - else - return result; -} - -One Compiler::Eq() -{ - One result = Rel(); - for(;;) - if(p.Char2('=', '=')) - result = Create(result, Rel()); - else - if(p.Char2('!', '=')) - result = Create(Rel(), result); - else - return result; -} - -One Compiler::And() -{ - One result = Eq(); - while(!p.IsChar2('&', '&') && p.Char('&')) - result = Create(result, Eq()); - return result; -} - -One Compiler::Xor() -{ - One result = And(); - while(p.Char('^')) - result = Create(result, And()); - return result; -} - -One Compiler::Or() -{ - One result = Xor(); - while(!p.IsChar2('|', '|') && p.Char('|')) - result = Create(result, Xor()); - return result; -} - -One Compiler::LogAnd() -{ - One result = Or(); - while(p.Char2('&', '&')) - result = Create(result, Or()); - return result; -} - -One Compiler::LogOr() -{ - One result = LogAnd(); - while(p.Char2('|', '|')) - result = Create(result, LogAnd()); - return result; -} - -One Compiler::Conditional() -{ - One result = LogOr(); - if(p.Char('?')) { - One r; - ExeCond& c = r.Create(); - c.cond = result; - c.ontrue = LogOr(); - p.PassChar(':'); - c.onfalse = LogOr(); - result = r; - } - return result; -} - -One Compiler::Exp() -{ - return Conditional(); -} - -void Compiler::ExeBlock::AddText(const char *b, const char *s) -{ - if(s > b) { - RawHtmlText t; - t.text = String(b, s); - item.Add().Create().value = RawToValue(t); - } -} - -One Compiler::Block() -{ - One result; - ExeBlock& blk = result.Create(); - const char *s = p.GetSpacePtr(); - const char *b = s; - int line = 1; - while(*s) { - if(*s == '$') { - if(s[1] == '$') - s += 2; - else { - blk.AddText(b, s); - p.Set(s + 1, NULL, line); - if(p.Id("if")) { - ExeCond& c = blk.item.Add().Create(); - p.PassChar('('); - c.cond = Exp(); - p.PassChar(')'); - c.ontrue = Block(); - if(p.Id("else")) - c.onfalse = Block(); - if(!p.Char('/')) - p.PassId("endif"); - } - else - if(p.Id("for")) { - ExeFor& c = blk.item.Add().Create(); - p.PassChar('('); - int q = var.GetCount(); - var.Add(p.ReadId()); - var.Add(Null); // LoopInfo placeholder - forvar.Add(true); - forvar.Add(true); - p.PassId("in"); - c.value = Exp(); - p.PassChar(')'); - c.body = Block(); - var.Trim(q); - forvar.SetCount(q); - if(p.Id("else")) - c.onempty = Block(); - if(!p.Char('/')) - p.PassId("endfor"); - } - else - if(p.IsId("else") || p.IsId("endif") || p.IsId("endfor") || p.IsChar('/')) - return result; - else - blk.item.AddPick(Prim()); - b = s = p.GetSpacePtr(); - line = p.GetLine(); - } - } - else - if(*s++ == '\n') - line++; - } - blk.AddText(b, s); - p.Set(s, NULL, line); - return result; -} - -One Compile(const char *code, const Index& vars) -{ - Compiler c(code, vars); - One exe = c.Block(); - LLOG("Before optimization node count: " << c.GetNodeCount(exe)); - c.Optimize(exe); - LLOG("After optimization node count: " << c.GetNodeCount(exe)); - return exe; -} - +#include "Skylark.h" + +#define LLOG(x) // DLOG(x) + +namespace Upp { + +Value Raw(const String& s) +{ + RawHtmlText r; + r.text = s; + return RawToValue(r); +} + +VectorMap&, const Renderer *)>& Compiler::functions() +{ + static VectorMap&, const Renderer *)> x; + return x; +} + +void Compiler::Register(const String& id, Value (*fn)(const Vector&, const Renderer *)) +{ + functions().GetAdd(id) = fn; +} + +int Compiler::ForVar(String id, int i) +{ + if(i + 1 < var.GetCount() && forvar[i]) + return i + 1; + p.ThrowError(id + " is not 'for' iterator"); + return 0; +} + +int CountLinkArgs(const Vector& part) +{ + int args = 0; + for(int i = 0; i < part.GetCount(); i++) { + int p = (byte)*part[i]; + if(p >= 0 && p < 30) + args = max(args, p + 1); + } + return args; +} + +One Compiler::Prim() +{ + One result; + if(p.Char('!')) + result = Create(Prim()); + else + if(p.Char('-')) + result = Create(Prim()); + else + if(p.Char('+')) + result = Prim(); + else + if(p.IsId() || p.IsChar('.') || p.IsChar('@')) { + String id; + if(p.Char('.')) + id = "."; + else + if(p.Char('@')) + id = "@"; + id << p.ReadId(); + int n = var.Find(id); + if(p.Char('(')) { + Value (*f)(const Vector&, const Renderer *) = functions().Get(id, NULL); + if(!f) { + Vector *part = GetUrlViewLinkParts(id); + if(!part) + p.ThrowError("function nor link not found '" + id + "'"); + ExeLink& ln = result.Create(); + ln.part = part; + if(!p.Char(')')) { + do + ln.arg.AddPick(Exp()); + while(p.Char(',')); + p.PassChar(')'); + } + if(CountLinkArgs(*part) > ln.arg.GetCount()) + p.ThrowError("invalid number of link arguments '" + id + "'"); + } + else { + ExeFn& fn = result.Create(); + fn.fn = f; + if(!p.Char(')')) { + do + fn.arg.AddPick(Exp()); + while(p.Char(',')); + p.PassChar(')'); + } + while(p.Char('.')) { + One r; + ExeField& f = r.Create(); + f.value = result; + f.id = p.ReadId(); + result = r; + } + } + return result; + } + if(n < 0) { + Vector *part = GetUrlViewLinkParts(id); + ExeConst& c = result.Create(); + if(!part) { + return result; + } + String l = "\"/"; + for(int i = 0; i < (*part).GetCount(); i++) { + if(i) + l << '/'; + l << UrlEncode((*part)[i]); + } + l << '\"'; + c.value = Raw(l); + } + else + if(p.Char('.')) { + if(p.Id("_first")) + result.Create().var_index = ForVar(id, n); + else + if(p.Id("_last")) + result.Create().var_index = ForVar(id, n); + else + if(p.Id("_index")) + result.Create().var_index = ForVar(id, n); + else + if(p.Id("_key")) + result.Create().var_index = ForVar(id, n); + else { + result.Create().var_index = n; + do { + One r; + ExeField& f = r.Create(); + f.value = result; + f.id = p.ReadId(); + result = r; + } + while(p.Char('.')); + } + } + else + result.Create().var_index = n; + } + else + if(p.Char('{')) { + ExeMap& m = result.Create(); + do { + m.key.AddPick(Exp()); + p.PassChar(':'); + m.value.AddPick(Exp()); + } + while(p.Char(',')); + p.PassChar('}'); + } + else + if(p.Char('[')) { + ExeArray& m = result.Create(); + do { + m.item.AddPick(Exp()); + } + while(p.Char(',')); + p.PassChar(']'); + } + else + if(p.Char('(')) { + result = Exp(); + p.PassChar(')'); + } + else { + ExeConst& c = result.Create(); + if(p.Char2('0', 'x') || p.Char2('0', 'X')) + c.value = (int)p.ReadNumber(16); + else + if(p.Char('0')) + c.value = int(p.IsNumber() ? p.ReadNumber(8) : 0); + else + c.value = p.IsString() ? Value(p.ReadString()) : Value(p.ReadDouble()); + } + return result; +} + +One Compiler::Mul() +{ + One result = Prim(); + for(;;) + if(p.Char('*')) + result = Create(result, Prim()); + else + if(p.Char('/')) + result = Create(result, Prim()); + else + if(p.Char('%')) + result = Create(result, Prim()); + else + return result; +} + +One Compiler::Add() +{ + One result = Mul(); + for(;;) + if(p.Char('+')) + result = Create(result, Mul()); + else + if(p.Char('-')) + result = Create(result, Mul()); + else + return result; +} + +One Compiler::Shift() +{ + One result = Add(); + for(;;) + if(p.Char3('>', '>', '>')) + result = Create(result, Add()); + else + if(p.Char2('>', '>')) + result = Create(result, Add()); + else + if(p.Char2('<', '<')) + result = Create(result, Add()); + else + return result; +} + +One Compiler::Rel() +{ + One result = Shift(); + for(;;) + if(p.Char2('<', '=')) + result = Create(result, Shift()); + else + if(p.Char2('>', '=')) + result = Create(Shift(), result); + else + if(p.Char('<')) + result = Create(result, Shift()); + else + if(p.Char('>')) + result = Create(Shift(), result); + else + return result; +} + +One Compiler::Eq() +{ + One result = Rel(); + for(;;) + if(p.Char2('=', '=')) + result = Create(result, Rel()); + else + if(p.Char2('!', '=')) + result = Create(Rel(), result); + else + return result; +} + +One Compiler::And() +{ + One result = Eq(); + while(!p.IsChar2('&', '&') && p.Char('&')) + result = Create(result, Eq()); + return result; +} + +One Compiler::Xor() +{ + One result = And(); + while(p.Char('^')) + result = Create(result, And()); + return result; +} + +One Compiler::Or() +{ + One result = Xor(); + while(!p.IsChar2('|', '|') && p.Char('|')) + result = Create(result, Xor()); + return result; +} + +One Compiler::LogAnd() +{ + One result = Or(); + while(p.Char2('&', '&')) + result = Create(result, Or()); + return result; +} + +One Compiler::LogOr() +{ + One result = LogAnd(); + while(p.Char2('|', '|')) + result = Create(result, LogAnd()); + return result; +} + +One Compiler::Conditional() +{ + One result = LogOr(); + if(p.Char('?')) { + One r; + ExeCond& c = r.Create(); + c.cond = result; + c.ontrue = LogOr(); + p.PassChar(':'); + c.onfalse = LogOr(); + result = r; + } + return result; +} + +One Compiler::Exp() +{ + return Conditional(); +} + +void Compiler::ExeBlock::AddText(const char *b, const char *s) +{ + if(s > b) { + RawHtmlText t; + t.text = String(b, s); + item.Add().Create().value = RawToValue(t); + } +} + +One Compiler::Block() +{ + One result; + ExeBlock& blk = result.Create(); + const char *s = p.GetSpacePtr(); + const char *b = s; + int line = 1; + while(*s) { + if(*s == '$') { + if(s[1] == '$') + s += 2; + else { + blk.AddText(b, s); + p.Set(s + 1, NULL, line); + if(p.Id("if")) { + ExeCond& c = blk.item.Add().Create(); + p.PassChar('('); + c.cond = Exp(); + p.PassChar(')'); + c.ontrue = Block(); + if(p.Id("else")) + c.onfalse = Block(); + if(!p.Char('/')) + p.PassId("endif"); + } + else + if(p.Id("for")) { + ExeFor& c = blk.item.Add().Create(); + p.PassChar('('); + int q = var.GetCount(); + var.Add(p.ReadId()); + var.Add(Null); // LoopInfo placeholder + forvar.Add(true); + forvar.Add(true); + p.PassId("in"); + c.value = Exp(); + p.PassChar(')'); + c.body = Block(); + var.Trim(q); + forvar.SetCount(q); + if(p.Id("else")) + c.onempty = Block(); + if(!p.Char('/')) + p.PassId("endfor"); + } + else + if(p.IsId("else") || p.IsId("endif") || p.IsId("endfor") || p.IsChar('/')) + return result; + else + blk.item.AddPick(Prim()); + b = s = p.GetSpacePtr(); + line = p.GetLine(); + } + } + else + if(*s++ == '\n') + line++; + } + blk.AddText(b, s); + p.Set(s, NULL, line); + return result; +} + +One Compile(const char *code, const Index& vars) +{ + Compiler c(code, vars); + One exe = c.Block(); + LLOG("Before optimization node count: " << c.GetNodeCount(exe)); + c.Optimize(exe); + LLOG("After optimization node count: " << c.GetNodeCount(exe)); + return exe; +} + }; \ No newline at end of file diff --git a/uppsrc/Skylark/Dispatch.cpp b/uppsrc/Skylark/Dispatch.cpp index 095ab0816..87fcac5ad 100644 --- a/uppsrc/Skylark/Dispatch.cpp +++ b/uppsrc/Skylark/Dispatch.cpp @@ -380,6 +380,8 @@ void Http::Dispatch(TcpSocket& socket) SKYLARKLOG("Redirect to: " << redirect); r << "HTTP/1.1 " << code << " Found\r\n"; r << "Location: " << redirect << "\r\n"; + for(int i = 0; i < cookies.GetCount(); i++) + r << cookies[i]; } else { r << @@ -390,8 +392,6 @@ void Http::Dispatch(TcpSocket& socket) "Connection: close\r\n" "Cache-Control: no-cache\r\n" "Content-Type: " << content_type << "\r\n"; - for(int i = 0; i < cookies.GetCount(); i++) - r << cookies[i]; r << "\r\n"; } socket.PutAll(r); diff --git a/uppsrc/Skylark/Http.cpp b/uppsrc/Skylark/Http.cpp index 9abff6b44..8fbd4b619 100644 --- a/uppsrc/Skylark/Http.cpp +++ b/uppsrc/Skylark/Http.cpp @@ -1,350 +1,350 @@ -#include "Skylark.h" - -#define LLOG(x) LOG(x) -#define LTIMING(x) RTIMING(x) - -namespace Upp { - -Http::Http(SkylarkApp& app) -: app(app) -{ - code = 200; - content_type = "text/html; charset=UTF-8"; - session_dirty = false; - lang = LNG_ENGLISH; -} - -void Http::ParseRequest(const char *p) -{ - while(*p) { - const char *last = p; - while(*p && *p != '=' && *p != '&') - p++; - String key = UrlDecode(last, p); - if(*p == '=') - p++; - last = p; - while(*p && *p != '&') - p++; - if(*key != '.' && *key != '@') - var.GetAdd(key) = UrlDecode(last, p); - if(*p) - p++; - } -} - -String HttpResponse(int code, const char *phrase, const String& data, const char *content_type, - const char *cookies) -{ - String r; - r << - "HTTP/1.0 " << code << ' ' << phrase << "\r\n" - "Date: " << WwwFormat(GetUtcTime()) << "\r\n" - "Server: U++\r\n" - "Content-Length: " << data.GetCount() << "\r\n" - "Connection: close\r\n" - << cookies; - if(content_type) - r << "Content-Type: " << content_type << "\r\n"; - r << "\r\n" << data; - return r; -} - -Http& Http::SetRawCookie(const char *id, const String& value, Time expires, - const char *path, const char *domain, bool secure, - bool httponly) -{ - if(*id == '.') - id++; - var.GetAdd('@' + id) = value; - String& c = cookies.GetAdd(id); - c.Clear(); - c << "Set-Cookie:" << ' ' << id << '=' << value; - if(!IsNull(expires)) - c << "; " << WwwFormat(expires); - c << "; Path=" << (path && *path ? path : "/"); - if(domain && *domain) - c << "; Domain=" << domain; - if(secure) - c << "; Secure"; - if(httponly) - c << "; HttpOnly"; - c << "\r\n"; - return *this; -} - -int Http::Int(const char *id) const -{ - Value v = operator[](id); - if(v.Is()) - return v; - if(IsString(v)) - return ScanInt((String)v); - if(IsNull(v)) - return Null; - if(IsNumber(v)) { - double d = v; - if(d > INT_MIN && d <= INT_MAX) - return (int)d; - } - return Null; -} - -int Http::Int(int i) const -{ - return ScanInt(operator[](i)); -} - -String HttpAsString(const Value& v) -{ - if(v.Is()) - return v.To().text; - return AsString(v); -} - -Http& Http::Content(const char *s, const Value& data) -{ - content_type = s; - response = HttpAsString(data); - return *this; -} - -Http& Http::operator<<(const Value& s) -{ - response << HttpAsString(s); - return *this; -} - -Http& Http::SetCookie(const char *id, const String& value, Time expires, - const char *path, const char *domain, bool secure, bool httponly) -{ - return SetRawCookie(id, UrlEncode(value), expires, path, domain, secure); -} - -void Http::ReadMultiPart(const String& buffer) -{ - const char *p = buffer; - while(p[0] != '-' || p[1] != '-') { - while(*p != '\n') - if(*p++ == 0) - return; // end of file, boundary not found - p++; - } - String delimiter; - { // read multipart delimiter - const char *b = (p += 2); - while(*p && *p++ != '\n') - ; - const char *e = p; - while(e > b && (byte)e[-1] <= ' ') - e--; - delimiter = String(b, e); - } - - int delta = 4 + delimiter.GetLength(); - const char *e = buffer.End(); - if(e - p < delta) - return; - e -= delta; - while(p < e) { // read individual parts - String filename, content_type, name; - while(!MemICmp(p, "content-", 8)) { // parse content specifiers - p += 8; - if(!MemICmp(p, "disposition:", 12)) { - p += 12; - while(*p && *p != '\n') - if((byte)*p <= ' ') - p++; - else { // fetch key-value pair - const char *kp = p; - while(*p && *p != '\n' && *p != '=' && *p != ';') - p++; - const char *ke = p; - String value; - if(*p == '=') { - const char *b = ++p; - if(*p == '\"') { // quoted value - b++; - while(*++p && *p != '\n' && *p != '\"') - ; - value = String(b, p); - if(*p == '\"') - p++; - } - else { - while(*p && *p != '\n' && *p != ';') - p++; - value = String(b, p); - } - } - if(ke - kp == 4 && !MemICmp(kp, "name", 4)) - name = value; - else if(ke - kp == 8 && !MemICmp(kp, "filename", 8)) - filename = value; - if(*p == ';') - p++; - } - } - else if(!MemICmp(p, "type:", 5)) { - p += 5; - while(*p && *p != '\n' && (byte)*p <= ' ') - p++; - const char *b = p; - while(*p && *p != '\n') - p++; - const char *e = p; - while(e > b && (byte)e[-1] <= ' ') - e--; - content_type = String(b, e); - } - ; - while(*p && *p++ != '\n') - ; - } - if(*p++ != '\r' || *p++ != '\n') - return; - const char *b = p; - while(p < e) { - p = (const char *)memchr(p, '\r', e - p); - if(!p) - return; - if(p[0] == '\r' && p[1] == '\n' && p[2] == '-' && p[3] == '-' - && !memcmp(p + 4, delimiter, delimiter.GetLength())) - break; - p++; - } - if(!name.IsEmpty() && *name != '.' && *name != '@') { // add variables - if(!filename.IsEmpty()) - var.GetAdd(name + ".filename") = filename; - if(!content_type.IsEmpty()) - var.GetAdd(name + ".content_type") = content_type; - var.Add(name, String(b, p)); - } - p += delta; - while(*p && *p++ != '\n') - ; - } -} - -static const char hex_digits[] = "0123456789ABCDEF"; - -void UrlEncode(StringBuffer& out, const String& s) -{ - static bool ok[256]; - ONCELOCK { - for(int ch = 0; ch < 256; ch++) - ok[ch] = IsAlNum(ch) || ch == ',' || ch == '.' || ch == '-' || ch == '_'; - } - const char *p = s, *e = s.End(); - for(; p < e; p++) - { - const char *b = p; - while(p < e && ok[(byte)*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]; - } -} - -void MakeLink(StringBuffer& out, const Vector& part, const Vector& arg) -{ - LTIMING("MakeLink"); - out.Cat("/"); - for(int i = 0; i < part.GetCount(); i++) { - const String& p = part[i]; - if(i) - out << '/'; - int q = (byte)*p; - if(q < 32) { - if(q >= 0 && q < arg.GetCount()) - UrlEncode(out, AsString(arg[q])); - } - else - UrlEncode(out, p); - } - bool get = false; - for(int i = 0; i < arg.GetCount(); i++) - if(IsValueMap(arg[i])) { - if(get) - out << '&'; - else - out << '?'; - get = true; - ValueMap m = arg[i]; - for(int i = 0; i < m.GetCount(); i++) { - if(i) - out << '&'; - UrlEncode(out, AsString(m.GetKeys()[i])); - out << '='; - UrlEncode(out, AsString(m.GetValues()[i])); - } - } -} - -Http& Http::RenderResult(const char *template_name) -{ - LTIMING("Render"); - response << UPP::Render(GetTemplate(template_name), this, var.GetValues()); - return *this; -} - -Http& Http::Redirect(void (*view)(Http&), const Vector& arg) -{ - Redirect(MakeLink(view, arg)); - return *this; -} - -Http& Http::Redirect(void (*view)(Http&)) -{ - Vector arg; - Redirect(view, arg); - return *this; -} - -Http& Http::Redirect(void (*view)(Http&), const Value& v1) -{ - Vector arg; - arg.Add(v1); - Redirect(view, arg); - return *this; -} - -Http& Http::Redirect(void (*view)(Http&), const Value& v1, const Value& v2) -{ - Vector arg; - arg.Add(v1); - arg.Add(v2); - Redirect(view, arg); - return *this; -} - -Http& Http::Ux(const char *id, const String& text) -{ - if(response.GetCount()) - response << '\1'; - response << id << ':' << text; - return *this; -} - -Http& Http::UxRender(const char *id, const char *template_name) -{ - return Ux(id, RenderString(template_name)); -} - -Http& Http::UxSet(const char *id, const String& value) -{ - return Ux(String(">") + id, value); -} - -Http& Http::UxRun(const String& js_code) -{ - return Ux("!", js_code); -} - +#include "Skylark.h" + +#define LLOG(x) // DLOG(x) +#define LTIMING(x) // RTIMING(x) + +namespace Upp { + +Http::Http(SkylarkApp& app) +: app(app) +{ + code = 200; + content_type = "text/html; charset=UTF-8"; + session_dirty = false; + lang = LNG_ENGLISH; +} + +void Http::ParseRequest(const char *p) +{ + while(*p) { + const char *last = p; + while(*p && *p != '=' && *p != '&') + p++; + String key = UrlDecode(last, p); + if(*p == '=') + p++; + last = p; + while(*p && *p != '&') + p++; + if(*key != '.' && *key != '@') + var.GetAdd(key) = UrlDecode(last, p); + if(*p) + p++; + } +} + +String HttpResponse(int code, const char *phrase, const String& data, const char *content_type, + const char *cookies) +{ + String r; + r << + "HTTP/1.0 " << code << ' ' << phrase << "\r\n" + "Date: " << WwwFormat(GetUtcTime()) << "\r\n" + "Server: U++\r\n" + "Content-Length: " << data.GetCount() << "\r\n" + "Connection: close\r\n" + << cookies; + if(content_type) + r << "Content-Type: " << content_type << "\r\n"; + r << "\r\n" << data; + return r; +} + +Http& Http::SetRawCookie(const char *id, const String& value, Time expires, + const char *path, const char *domain, bool secure, + bool httponly) +{ + if(*id == '.') + id++; + var.GetAdd('@' + id) = value; + String& c = cookies.GetAdd(id); + c.Clear(); + c << "Set-Cookie:" << ' ' << id << '=' << value; + if(!IsNull(expires)) + c << "; " << WwwFormat(expires); + c << "; Path=" << (path && *path ? path : "/"); + if(domain && *domain) + c << "; Domain=" << domain; + if(secure) + c << "; Secure"; + if(httponly) + c << "; HttpOnly"; + c << "\r\n"; + return *this; +} + +int Http::Int(const char *id) const +{ + Value v = operator[](id); + if(v.Is()) + return v; + if(IsString(v)) + return ScanInt((String)v); + if(IsNull(v)) + return Null; + if(IsNumber(v)) { + double d = v; + if(d > INT_MIN && d <= INT_MAX) + return (int)d; + } + return Null; +} + +int Http::Int(int i) const +{ + return ScanInt(operator[](i)); +} + +String HttpAsString(const Value& v) +{ + if(v.Is()) + return v.To().text; + return AsString(v); +} + +Http& Http::Content(const char *s, const Value& data) +{ + content_type = s; + response = HttpAsString(data); + return *this; +} + +Http& Http::operator<<(const Value& s) +{ + response << HttpAsString(s); + return *this; +} + +Http& Http::SetCookie(const char *id, const String& value, Time expires, + const char *path, const char *domain, bool secure, bool httponly) +{ + return SetRawCookie(id, UrlEncode(value), expires, path, domain, secure); +} + +void Http::ReadMultiPart(const String& buffer) +{ + const char *p = buffer; + while(p[0] != '-' || p[1] != '-') { + while(*p != '\n') + if(*p++ == 0) + return; // end of file, boundary not found + p++; + } + String delimiter; + { // read multipart delimiter + const char *b = (p += 2); + while(*p && *p++ != '\n') + ; + const char *e = p; + while(e > b && (byte)e[-1] <= ' ') + e--; + delimiter = String(b, e); + } + + int delta = 4 + delimiter.GetLength(); + const char *e = buffer.End(); + if(e - p < delta) + return; + e -= delta; + while(p < e) { // read individual parts + String filename, content_type, name; + while(!MemICmp(p, "content-", 8)) { // parse content specifiers + p += 8; + if(!MemICmp(p, "disposition:", 12)) { + p += 12; + while(*p && *p != '\n') + if((byte)*p <= ' ') + p++; + else { // fetch key-value pair + const char *kp = p; + while(*p && *p != '\n' && *p != '=' && *p != ';') + p++; + const char *ke = p; + String value; + if(*p == '=') { + const char *b = ++p; + if(*p == '\"') { // quoted value + b++; + while(*++p && *p != '\n' && *p != '\"') + ; + value = String(b, p); + if(*p == '\"') + p++; + } + else { + while(*p && *p != '\n' && *p != ';') + p++; + value = String(b, p); + } + } + if(ke - kp == 4 && !MemICmp(kp, "name", 4)) + name = value; + else if(ke - kp == 8 && !MemICmp(kp, "filename", 8)) + filename = value; + if(*p == ';') + p++; + } + } + else if(!MemICmp(p, "type:", 5)) { + p += 5; + while(*p && *p != '\n' && (byte)*p <= ' ') + p++; + const char *b = p; + while(*p && *p != '\n') + p++; + const char *e = p; + while(e > b && (byte)e[-1] <= ' ') + e--; + content_type = String(b, e); + } + ; + while(*p && *p++ != '\n') + ; + } + if(*p++ != '\r' || *p++ != '\n') + return; + const char *b = p; + while(p < e) { + p = (const char *)memchr(p, '\r', e - p); + if(!p) + return; + if(p[0] == '\r' && p[1] == '\n' && p[2] == '-' && p[3] == '-' + && !memcmp(p + 4, delimiter, delimiter.GetLength())) + break; + p++; + } + if(!name.IsEmpty() && *name != '.' && *name != '@') { // add variables + if(!filename.IsEmpty()) + var.GetAdd(name + ".filename") = filename; + if(!content_type.IsEmpty()) + var.GetAdd(name + ".content_type") = content_type; + var.Add(name, String(b, p)); + } + p += delta; + while(*p && *p++ != '\n') + ; + } +} + +static const char hex_digits[] = "0123456789ABCDEF"; + +void UrlEncode(StringBuffer& out, const String& s) +{ + static bool ok[256]; + ONCELOCK { + for(int ch = 0; ch < 256; ch++) + ok[ch] = IsAlNum(ch) || ch == ',' || ch == '.' || ch == '-' || ch == '_'; + } + const char *p = s, *e = s.End(); + for(; p < e; p++) + { + const char *b = p; + while(p < e && ok[(byte)*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]; + } +} + +void MakeLink(StringBuffer& out, const Vector& part, const Vector& arg) +{ + LTIMING("MakeLink"); + out.Cat("/"); + for(int i = 0; i < part.GetCount(); i++) { + const String& p = part[i]; + if(i) + out << '/'; + int q = (byte)*p; + if(q < 32) { + if(q >= 0 && q < arg.GetCount()) + UrlEncode(out, AsString(arg[q])); + } + else + UrlEncode(out, p); + } + bool get = false; + for(int i = 0; i < arg.GetCount(); i++) + if(IsValueMap(arg[i])) { + if(get) + out << '&'; + else + out << '?'; + get = true; + ValueMap m = arg[i]; + for(int i = 0; i < m.GetCount(); i++) { + if(i) + out << '&'; + UrlEncode(out, AsString(m.GetKeys()[i])); + out << '='; + UrlEncode(out, AsString(m.GetValues()[i])); + } + } +} + +Http& Http::RenderResult(const char *template_name) +{ + LTIMING("Render"); + response << UPP::Render(GetTemplate(template_name), this, var.GetValues()); + return *this; +} + +Http& Http::Redirect(void (*view)(Http&), const Vector& arg) +{ + Redirect(MakeLink(view, arg)); + return *this; +} + +Http& Http::Redirect(void (*view)(Http&)) +{ + Vector arg; + Redirect(view, arg); + return *this; +} + +Http& Http::Redirect(void (*view)(Http&), const Value& v1) +{ + Vector arg; + arg.Add(v1); + Redirect(view, arg); + return *this; +} + +Http& Http::Redirect(void (*view)(Http&), const Value& v1, const Value& v2) +{ + Vector arg; + arg.Add(v1); + arg.Add(v2); + Redirect(view, arg); + return *this; +} + +Http& Http::Ux(const char *id, const String& text) +{ + if(response.GetCount()) + response << '\1'; + response << id << ':' << text; + return *this; +} + +Http& Http::UxRender(const char *id, const char *template_name) +{ + return Ux(id, RenderString(template_name)); +} + +Http& Http::UxSet(const char *id, const String& value) +{ + return Ux(String(">") + id, value); +} + +Http& Http::UxRun(const String& js_code) +{ + return Ux("!", js_code); +} + }; \ No newline at end of file diff --git a/uppsrc/Skylark/Renderer.cpp b/uppsrc/Skylark/Renderer.cpp index eb42a68f7..3e71bb619 100644 --- a/uppsrc/Skylark/Renderer.cpp +++ b/uppsrc/Skylark/Renderer.cpp @@ -1,82 +1,81 @@ -#include "Skylark.h" - -#define LLOG(x) // DLOG(x) -#define LTIMING(x) // RTIMING(x) - -namespace Upp { - -Renderer& Renderer::operator()(const ValueMap& map) -{ - ValueArray v = map.GetValues(); - const Index& k = map.GetKeys(); - for(int i = 0; i < map.GetCount(); i++) - var.Add(k[i], v[i]); - return *this; -} - -Renderer& Renderer::Link(const char *id, void (*view)(Http&), const Vector& arg) -{ - var.Add(id, Raw('\"' + MakeLink(view, arg) + '\"')); - return *this; -} - -Renderer& Renderer::operator()(const char *id, void (*view)(Http&)) -{ - return Link(id, view, Vector()); -} - -Renderer& Renderer::operator()(const char *id, void (*view)(Http&), const Value& arg1) -{ - return Link(id, view, Vector() << arg1); -} - -Renderer& Renderer::operator()(const char *id, void (*view)(Http&), const Value& arg1, const Value& arg2) -{ - return Link(id, view, Vector() << arg1 << arg2); -} - -StaticMutex template_cache_lock; -ArrayMap > template_cache; - -const One& Renderer::GetTemplate(const char *template_name) -{ - DDUMPM(var); - LTIMING("GetTemplate"); - StringBuffer s; - { - LTIMING("MakeSignature"); - for(int i = 0; i < var.GetCount(); i++) - s << var.GetKey(i) << ';'; - s << ':' << template_name; - } - if(!SkylarkApp::Config().use_caching) // Templates get overwritten is not cached, MT hazard - s << ';' << Thread::GetCurrentId(); - String sgn = s; - LLOG("Trying to retrieve " << sgn << " from cache"); - Mutex::Lock __(template_cache_lock); - int q = template_cache.Find(sgn); - if(q >= 0 && SkylarkApp::Config().use_caching) - return template_cache[q]; - LLOG("About to compile: " << sgn); - LTIMING("Compile"); - One& exe = q >= 0 ? template_cache[q] : template_cache.Add(sgn); - exe = Compile(GetPreprocessedTemplate(template_name, lang), var.GetIndex()); - return exe; -} - -String Renderer::RenderString(const String& template_name) -{ - return UPP::Render(GetTemplate(template_name), this, var.GetValues()); -} - -Renderer& Renderer::Render(const char *id, const String& template_name) -{ - var.Add(id, Render(template_name)); - return *this; -} - -Renderer::~Renderer() -{ -} - +#include "Skylark.h" + +#define LLOG(x) // DLOG(x) +#define LTIMING(x) // RTIMING(x) + +namespace Upp { + +Renderer& Renderer::operator()(const ValueMap& map) +{ + ValueArray v = map.GetValues(); + const Index& k = map.GetKeys(); + for(int i = 0; i < map.GetCount(); i++) + var.Add(k[i], v[i]); + return *this; +} + +Renderer& Renderer::Link(const char *id, void (*view)(Http&), const Vector& arg) +{ + var.Add(id, Raw('\"' + MakeLink(view, arg) + '\"')); + return *this; +} + +Renderer& Renderer::operator()(const char *id, void (*view)(Http&)) +{ + return Link(id, view, Vector()); +} + +Renderer& Renderer::operator()(const char *id, void (*view)(Http&), const Value& arg1) +{ + return Link(id, view, Vector() << arg1); +} + +Renderer& Renderer::operator()(const char *id, void (*view)(Http&), const Value& arg1, const Value& arg2) +{ + return Link(id, view, Vector() << arg1 << arg2); +} + +StaticMutex template_cache_lock; +ArrayMap > template_cache; + +const One& Renderer::GetTemplate(const char *template_name) +{ + LTIMING("GetTemplate"); + StringBuffer s; + { + LTIMING("MakeSignature"); + for(int i = 0; i < var.GetCount(); i++) + s << var.GetKey(i) << ';'; + s << ':' << template_name; + } + if(!SkylarkApp::Config().use_caching) // Templates get overwritten is not cached, MT hazard + s << ';' << Thread::GetCurrentId(); + String sgn = s; + LLOG("Trying to retrieve " << sgn << " from cache"); + Mutex::Lock __(template_cache_lock); + int q = template_cache.Find(sgn); + if(q >= 0 && SkylarkApp::Config().use_caching) + return template_cache[q]; + LLOG("About to compile: " << sgn); + LTIMING("Compile"); + One& exe = q >= 0 ? template_cache[q] : template_cache.Add(sgn); + exe = Compile(GetPreprocessedTemplate(template_name, lang), var.GetIndex()); + return exe; +} + +String Renderer::RenderString(const String& template_name) +{ + return UPP::Render(GetTemplate(template_name), this, var.GetValues()); +} + +Renderer& Renderer::Render(const char *id, const String& template_name) +{ + var.Add(id, Render(template_name)); + return *this; +} + +Renderer::~Renderer() +{ +} + }; \ No newline at end of file diff --git a/uppsrc/Skylark/Session.cpp b/uppsrc/Skylark/Session.cpp index 1e967313e..e8ba9cf4f 100644 --- a/uppsrc/Skylark/Session.cpp +++ b/uppsrc/Skylark/Session.cpp @@ -1,166 +1,165 @@ -#include "Skylark.h" - -#define LLOG(x) //DLOG(x) -#define LDUMPC(x) //DDUMPC(x) -#define LDUMPM(x) //DDUMPM(x) -#define LLOGHEX(x) //DLOGHEX(x) - -namespace Upp { - -SessionConfig::SessionConfig() -{ - cookie = "__skylark_session_cookie__"; - dir = ConfigFile("session"); - format = SESSION_FORMAT_BINARY; - id_column = "ID"; - data_column = "DATA"; - lastwrite_column = "LASTWRITE"; - expire = 3600 * 24 * 365; // one year to expire the session -} - -String Http::SessionFile(const String& sid) -{ - ONCELOCK - RealizeDirectory(app.session.dir); - return AppendFileName(app.session.dir, sid); -} - -void Http::LoadSession() -{ - const SessionConfig& cfg = app.session; - session_var.Clear(); - session_id = (*this)['@' + cfg.cookie]; - if(IsNull(session_id)) - return; - String data; - if(cfg.table.IsNull()) - data = LoadFile(SessionFile(session_id)); - else - data = SQLR % Select(cfg.data_column).From(cfg.table) - .Where(cfg.id_column == session_id); - LLOGHEX(data); - switch(cfg.format) { - case SESSION_FORMAT_JSON: - LoadFromJson(session_var, data); - break; - case SESSION_FORMAT_XML: - LoadFromXML(session_var, data); - break; - case SESSION_FORMAT_BINARY: - LoadFromString(session_var, data); - break; - } - SKYLARKLOG("Loaded session: " << session_id); - LDUMPM(session_var); - for(int i = 0; i < session_var.GetCount(); i++) - var.GetAdd('.' + session_var.GetKey(i)) = session_var[i]; -} - -thread__ int s_exp; - -static int s_last_expiration_check; - -StaticMutex expire_mutex; - -void Http::SaveSession() -{ - const SessionConfig& cfg = app.session; - SetCookie(cfg.cookie, session_id); - if(IsNull(session_id)) - return; - String data; - switch(cfg.format) { - case SESSION_FORMAT_JSON: - data = StoreAsJson(session_var); - break; - case SESSION_FORMAT_XML: - data = StoreAsXML(session_var, "session"); - break; - case SESSION_FORMAT_BINARY: - data = StoreAsString(session_var); - break; - } - if(cfg.table.IsNull()) - SaveFile(SessionFile(session_id), data); - else { - SqlVal d = SqlBinary(data); - Time tm = GetSysTime(); - SQL * Update(cfg.table) - (cfg.data_column, d) - (cfg.lastwrite_column, tm) - .Where(cfg.id_column == session_id); - if(SQL.GetRowsProcessed() == 0) - SQL * Insert(cfg.table) - (cfg.id_column, session_id) - (cfg.data_column, d) - (cfg.lastwrite_column, tm); - } - SKYLARKLOG("Stored session: " << session_id); - LDUMPM(session_var); - - if((s_exp++ % 1000) == 0) { - bool expire_sessions = false; - { - Mutex::Lock __(expire_mutex); - if((dword)msecs(s_last_expiration_check) > 1000 * 60 * 10) { - expire_sessions = true; - s_last_expiration_check = msecs(); - } - } - if(expire_sessions) { - Time tm = GetSysTime() - cfg.expire; - SKYLARKLOG("Expiring sessions older than " << tm); - if(cfg.table.IsNull()) { - FindFile ff(AppendFileName(cfg.dir, "*.*")); - Vector todelete; - while(ff) { - if(ff.GetLastWriteTime() < tm) - todelete.Add(ff.GetPath()); - ff.Next(); - } - for(int i = 0; i < todelete.GetCount(); i++) - FileDelete(todelete[i]); - } - else - SQL * Delete(cfg.table).Where(cfg.lastwrite_column < tm); - } - } -} - -Http& Http::ClearSession() -{ - session_var.Clear(); - session_id.Clear(); - return *this; -} - -Http& Http::SessionSet(const char *id, const Value& value) -{ - LLOG("SessionSet " << id << " = " << value); - if(*id == '.') - id++; - if(IsNull(session_id)) - NewSessionId(); - session_var.GetAdd(id) = value; - var.GetAdd('.' + id) = value; - session_dirty = true; - return *this; -} - -Http& Http::NewSessionId() -{ - session_id = AsString(Uuid::Create()); - session_dirty = true; - return *this; -} - -Http& Http::SetLanguage(int lang_) -{ - lang = lang_; - Upp::SetLanguage(lang_); - SessionSet("__lang__", lang); - SessionSet("language", ToLower(LNGAsText(lang))); - return *this; -} - +#include "Skylark.h" + +#define LLOG(x) //DLOG(x) +#define LDUMPC(x) //DDUMPC(x) +#define LDUMPM(x) //DDUMPM(x) +#define LLOGHEX(x) //DLOGHEX(x) + +namespace Upp { + +SessionConfig::SessionConfig() +{ + cookie = "__skylark_session_cookie__"; + dir = ConfigFile("session"); + format = SESSION_FORMAT_BINARY; + id_column = "ID"; + data_column = "DATA"; + lastwrite_column = "LASTWRITE"; + expire = 3600 * 24 * 365; // one year to expire the session +} + +String Http::SessionFile(const String& sid) +{ + ONCELOCK + RealizeDirectory(app.session.dir); + return AppendFileName(app.session.dir, sid); +} + +void Http::LoadSession() +{ + const SessionConfig& cfg = app.session; + session_var.Clear(); + session_id = (*this)['@' + cfg.cookie]; + if(IsNull(session_id)) + return; + String data; + if(cfg.table.IsNull()) + data = LoadFile(SessionFile(session_id)); + else + data = SQLR % Select(cfg.data_column).From(cfg.table) + .Where(cfg.id_column == session_id); + switch(cfg.format) { + case SESSION_FORMAT_JSON: + LoadFromJson(session_var, data); + break; + case SESSION_FORMAT_XML: + LoadFromXML(session_var, data); + break; + case SESSION_FORMAT_BINARY: + LoadFromString(session_var, data); + break; + } + SKYLARKLOG("Loaded session: " << session_id); + LDUMPM(session_var); + for(int i = 0; i < session_var.GetCount(); i++) + var.GetAdd('.' + session_var.GetKey(i)) = session_var[i]; +} + +thread__ int s_exp; + +static int s_last_expiration_check; + +StaticMutex expire_mutex; + +void Http::SaveSession() +{ + const SessionConfig& cfg = app.session; + SetCookie(cfg.cookie, session_id); + if(IsNull(session_id)) + return; + String data; + switch(cfg.format) { + case SESSION_FORMAT_JSON: + data = StoreAsJson(session_var); + break; + case SESSION_FORMAT_XML: + data = StoreAsXML(session_var, "session"); + break; + case SESSION_FORMAT_BINARY: + data = StoreAsString(session_var); + break; + } + if(cfg.table.IsNull()) + SaveFile(SessionFile(session_id), data); + else { + SqlVal d = SqlBinary(data); + Time tm = GetSysTime(); + SQL * Update(cfg.table) + (cfg.data_column, d) + (cfg.lastwrite_column, tm) + .Where(cfg.id_column == session_id); + if(SQL.GetRowsProcessed() == 0) + SQL * Insert(cfg.table) + (cfg.id_column, session_id) + (cfg.data_column, d) + (cfg.lastwrite_column, tm); + } + SKYLARKLOG("Stored session: " << session_id); + LDUMPM(session_var); + + if((s_exp++ % 1000) == 0) { + bool expire_sessions = false; + { + Mutex::Lock __(expire_mutex); + if((dword)msecs(s_last_expiration_check) > 1000 * 60 * 10) { + expire_sessions = true; + s_last_expiration_check = msecs(); + } + } + if(expire_sessions) { + Time tm = GetSysTime() - cfg.expire; + SKYLARKLOG("Expiring sessions older than " << tm); + if(cfg.table.IsNull()) { + FindFile ff(AppendFileName(cfg.dir, "*.*")); + Vector todelete; + while(ff) { + if(ff.GetLastWriteTime() < tm) + todelete.Add(ff.GetPath()); + ff.Next(); + } + for(int i = 0; i < todelete.GetCount(); i++) + FileDelete(todelete[i]); + } + else + SQL * Delete(cfg.table).Where(cfg.lastwrite_column < tm); + } + } +} + +Http& Http::ClearSession() +{ + session_var.Clear(); + session_id.Clear(); + return *this; +} + +Http& Http::SessionSet(const char *id, const Value& value) +{ + LLOG("SessionSet " << id << " = " << value); + if(*id == '.') + id++; + if(IsNull(session_id)) + NewSessionId(); + session_var.GetAdd(id) = value; + var.GetAdd('.' + id) = value; + session_dirty = true; + return *this; +} + +Http& Http::NewSessionId() +{ + session_id = AsString(Uuid::Create()); + session_dirty = true; + return *this; +} + +Http& Http::SetLanguage(int lang_) +{ + lang = lang_; + Upp::SetLanguage(lang_); + SessionSet("__lang__", lang); + SessionSet("language", ToLower(LNGAsText(lang))); + return *this; +} + }; \ No newline at end of file