From 03315adfa662983abd858910729efc03d172413f Mon Sep 17 00:00:00 2001 From: "Adam D. Ruppe" Date: Sun, 7 Jul 2019 22:44:11 -0400 Subject: [PATCH] so much amazing stuff --- cgi.d | 46 +++++++++++++++++++++++++++++++++++++------ database.d | 13 ++++++++++-- database_generation.d | 31 ++++++++++++++++++++++++++--- http2.d | 20 +++++++++++++++++++ mssql.d | 5 +++++ mysql.d | 3 +++ postgres.d | 4 ++++ sqlite.d | 4 ++++ webtemplate.d | 24 ++++++++++++++++++++++ 9 files changed, 139 insertions(+), 11 deletions(-) diff --git a/cgi.d b/cgi.d index f71923d..bb3e144 100644 --- a/cgi.d +++ b/cgi.d @@ -2746,9 +2746,28 @@ struct Uri { } } + n.removeDots(); + return n; } + void removeDots() { + auto parts = this.path.split("/"); + string[] toKeep; + foreach(part; parts) { + if(part == ".") { + continue; + } else if(part == "..") { + toKeep = toKeep[0 .. $-1]; + continue; + } else { + toKeep ~= part; + } + } + + this.path = toKeep.join("/"); + } + unittest { auto uri = Uri("test.html"); assert(uri.path == "test.html"); @@ -2810,6 +2829,10 @@ struct Uri { assert(url.basedOn(Uri("http://test.com/what/test.html?a=b&c=d#what")) == "http://test.com/what/test.html?query=answer"); assert(url.basedOn(Uri("http://test.com")) == "http://test.com?query=answer"); + url = Uri("/test/bar"); + assert(Uri("./").basedOn(url) == "/test/", Uri("./").basedOn(url)); + assert(Uri("../").basedOn(url) == "/"); + //auto uriBefore = url; url = Uri("#anchor"); // everything should remain the same except the anchor //uriBefore.anchor = "anchor"); @@ -6458,16 +6481,22 @@ auto callFromCgi(alias method, T)(T dg, Cgi cgi) { auto ident = idents[idx]; if(cgi.requestMethod == Cgi.RequestMethod.GET) { if(ident !in cgi.get) { - static if(is(defaults[idx] == void)) - throw new MissingArgumentException(__traits(identifier, method), ident, param.stringof); - else + static if(is(defaults[idx] == void)) { + static if(is(param == bool)) + params[idx] = false; + else + throw new MissingArgumentException(__traits(identifier, method), ident, param.stringof); + } else params[idx] = defaults[idx]; } } else { if(ident !in cgi.post) { - static if(is(defaults[idx] == void)) - throw new MissingArgumentException(__traits(identifier, method), ident, param.stringof); - else + static if(is(defaults[idx] == void)) { + static if(is(param == bool)) + params[idx] = false; + else + throw new MissingArgumentException(__traits(identifier, method), ident, param.stringof); + } else params[idx] = defaults[idx]; } } @@ -6510,6 +6539,9 @@ auto callFromCgi(alias method, T)(T dg, Cgi cgi) { } else static if(isSomeString!T || isIntegral!T || isFloatingPoint!T) { *what = to!T(value); return true; + } else static if(is(T == bool)) { + *what = value == "1" || value == "yes" || value == "t" || value == "true" || value == "on"; + return true; } else static if(is(T == K[], K)) { K tmp; if(name == paramName) { @@ -6552,6 +6584,8 @@ auto callFromCgi(alias method, T)(T dg, Cgi cgi) { auto k = to!K(insideBrackets); V v; + if(auto ptr = k in *what) + v = *ptr; name = name[0 .. paramName.length]; //writeln(name, afterName, " ", paramName); diff --git a/database.d b/database.d index b86d4e4..94bf103 100644 --- a/database.d +++ b/database.d @@ -3,6 +3,7 @@ module arsd.database; public import std.variant; import std.string; +public import std.datetime; /* Database 2.0 plan, WIP: @@ -48,6 +49,9 @@ interface Database { return queryImpl(sql, args); } + /// turns a systime into a value understandable by the target database as a timestamp to be concated into a query. so it should be quoted and escaped etc as necessary + string sysTimeToValue(SysTime); + /// Prepared statement api /* PreparedStatement prepareStatement(string sql, int numberOfArguments); @@ -355,9 +359,14 @@ class SelectBuilder : SqlBuilder { // used in the internal placeholder thing string toSql(Database db, Variant a) { auto v = a.peek!(void*); - if(v && (*v is null)) + if(v && (*v is null)) { return "NULL"; - else { + } else if(auto t = a.peek!(SysTime)) { + return db.sysTimeToValue(*t); + } else if(auto t = a.peek!(DateTime)) { + // FIXME: this might be broken cuz of timezones! + return db.sysTimeToValue(cast(SysTime) *t); + } else { string str = to!string(a); return '\'' ~ db.escape(str) ~ '\''; } diff --git a/database_generation.d b/database_generation.d index d136236..4a2313b 100644 --- a/database_generation.d +++ b/database_generation.d @@ -48,10 +48,29 @@ struct Nullable(T) { isNull = false; value = v; } + + T toArsdJsvar() { return value; } } struct Timestamp { string value; + string toArsdJsvar() { return value; } + + // FIXME: timezone + static Timestamp fromStrings(string date, string time) { + if(time.length < 6) + time ~= ":00"; + import std.datetime; + return Timestamp(SysTime.fromISOExtString(date ~ "T" ~ time).toISOExtString()); + } +} + +SysTime parseDbTimestamp(string s) { + if(s.length == 0) return SysTime.init; + auto date = s[0 .. 10]; + auto time = s[11 .. 20]; + auto tz = s[20 .. $]; + return SysTime.fromISOExtString(date ~ "T" ~ time ~ tz); } struct Constraint(string sql) {} @@ -61,6 +80,9 @@ struct UniqueIndex(Fields...) {} struct Serial { int value; + int toArsdJsvar() { return value; } + int getValue() { return value; } + alias getValue this; } @@ -148,6 +170,7 @@ string generateCreateTableFor(alias O)() { static foreach(attr; __traits(getAttributes, member)) { static if(is(typeof(attr) == Default)) { + // FIXME: postgresism there, try current_timestamp in sqlite sql ~= " DEFAULT " ~ attr.sql; } else static if(is(attr == Unique)) { sql ~= " UNIQUE"; @@ -282,9 +305,11 @@ void insert(O)(ref O t, Database db) { builder.addVariable(memberName, __traits(getMember, t, memberName)); else static if(is(T == bool)) builder.addVariable(memberName, __traits(getMember, t, memberName)); - else static if(is(T == Timestamp)) - {} // skipping... for now at least - else static if(is(T == enum)) + else static if(is(T == Timestamp)) { + auto v = __traits(getMember, t, memberName).value; + if(v.length) + builder.addVariable(memberName, v); + } else static if(is(T == enum)) builder.addVariable(memberName, cast(int) __traits(getMember, t, memberName)); } }} diff --git a/http2.d b/http2.d index 04ddece..21fcc11 100644 --- a/http2.d +++ b/http2.d @@ -514,8 +514,28 @@ struct Uri { } } + n.removeDots(); + return n; } + + void removeDots() { + auto parts = this.path.split("/"); + string[] toKeep; + foreach(part; parts) { + if(part == ".") { + continue; + } else if(part == "..") { + toKeep = toKeep[0 .. $-1]; + continue; + } else { + toKeep ~= part; + } + } + + this.path = toKeep.join("/"); + } + } /* diff --git a/mssql.d b/mssql.d index 1cbc9b3..c744eef 100644 --- a/mssql.d +++ b/mssql.d @@ -49,6 +49,11 @@ class MsSql : Database { query("START TRANSACTION"); } + // possible fixme, idk if this is right + override string sysTimeToValue(SysTime s) { + return "'" ~ escape(s.toISOExtString()) ~ "'"; + } + ResultSet queryImpl(string sql, Variant[] args...) { sql = escapedVariants(this, sql, args); diff --git a/mysql.d b/mysql.d index 14a5647..b565226 100644 --- a/mysql.d +++ b/mysql.d @@ -74,6 +74,9 @@ class MySqlResult : ResultSet { mysql_free_result(result); } + string sysTimeToValue(SysTime s) { + return "cast('" ~ escape(s.toISOExtString()) ~ "' as datetime)"; + } MYSQL_FIELD[] fields() { int numFields = mysql_num_fields(result); diff --git a/postgres.d b/postgres.d index 2ffb423..3607ec0 100644 --- a/postgres.d +++ b/postgres.d @@ -41,6 +41,10 @@ class PostgreSql : Database { PQfinish(conn); } + string sysTimeToValue(SysTime s) { + return "'" ~ escape(s.toISOExtString()) ~ "'::timestamptz"; + } + /** Prepared statement support diff --git a/sqlite.d b/sqlite.d index acd7f7c..8348651 100644 --- a/sqlite.d +++ b/sqlite.d @@ -91,6 +91,10 @@ class Sqlite : Database { throw new DatabaseException(error()); } + string sysTimeToValue(SysTime s) { + return "datetime('" ~ escape(s.toISOExtString()) ~ "')"; + } + // my extension for easier editing version(sqlite_extended_metadata_available) { ResultByDataObject queryDataObject(T...)(string sql, T t) { diff --git a/webtemplate.d b/webtemplate.d index 445f92c..1b6bd4d 100644 --- a/webtemplate.d +++ b/webtemplate.d @@ -39,6 +39,30 @@ Document renderTemplate(string templateName, var context = var.emptyObject, var return encodeComponent(f.get!string); }; + context.formatDate = function string(string s) { + if(s.length < 10) + return s; + auto year = s[0 .. 4]; + auto month = s[5 .. 7]; + auto day = s[8 .. 10]; + + return month ~ "/" ~ day ~ "/" ~ year; + }; + + context.formatTime = function string(string s) { + if(s.length < 20) + return s; + auto hour = s[11 .. 13].to!int; + auto minutes = s[14 .. 16].to!int; + auto seconds = s[17 .. 19].to!int; + + auto am = (hour >= 12) ? "PM" : "AM"; + if(hour > 12) + hour -= 12; + + return hour.to!string ~ (minutes < 10 ? ":0" : ":") ~ minutes.to!string ~ " " ~ am; + }; + auto skeleton = new Document(readText("templates/skeleton.html"), true, true); auto document = new Document(); document.parseSawAspCode = (string) => true; // enable adding <% %> to the dom