From 0a934ecc38904885be13723229b26d4254d4bb03 Mon Sep 17 00:00:00 2001 From: visor Date: Tue, 23 Apr 2013 00:31:18 +0900 Subject: [PATCH] add set-header function. update output-header function. add frame-options parameter. --- lib/app.cc | 23 ++++++++++++++ lib/app.h | 8 +++++ lib/http.cc | 61 ++++++++++++++++++++++++++---------- lib/http.h | 14 +++++++-- lib/httpconst.h | 1 + lib/motorenv.cc | 28 ++++++++++++++--- lib/motorenv.h | 3 +- modules/ml-apache.cc | 54 -------------------------------- modules/ml-apache.h | 1 - modules/ml-motor.cc | 88 ++++++++++++++++++++++++++++++++++++++++++++++++---- modules/ml-motor.h | 2 ++ 11 files changed, 198 insertions(+), 85 deletions(-) diff --git a/lib/app.cc b/lib/app.cc index 42c9434..7347e9d 100644 --- a/lib/app.cc +++ b/lib/app.cc @@ -42,6 +42,7 @@ static bool pcmp (char** a, const char* b, int n) { |random-cookie| |random-url| |no-cache| +|frame-options:[DENY|SAMEORIGIN]| |dump| */ @@ -129,6 +130,14 @@ void AppEnv::readOption (int argc, char** argv, MotorEnv* env) { cacheControl = CC_URL; } else if (zcmp (p, "no-cache")) { cacheControl = CC_NOCACHE; + } else if (cmp (p, "frame-options:")) { + if (zcmp (p, "DENY")) { + frameOpt = FOPT_DENY; + } else if (zcmp (p, "SAMEORIGIN")) { + frameOpt = FOPT_SAMEORIGIN; + } else { + throw (ustring (CharConst ("frame-options:")) + p + ustring (CharConst (" bad option."))); + } } else if (zcmp (p, "dump")) { debugDump = true; } else if (p[0] == ';' || p[0] == '(' || p[0] == '<') { @@ -201,6 +210,20 @@ void AppEnv::dump (std::ostream& out) { out << " no-cache\n"; break; } + switch (frameOpt) { + case FOPT_NONE: + break; + case FOPT_DENY: + out << " frame-options:DENY\n"; + break; + case FOPT_SAMEORIGIN: + out << " frame-options:SAMEORIGIN\n"; + break; + case FOPT_ALLOWFROM: + out << " frame-options:ALLOW-FROM " << foptUri << "\n"; + break; + default:; + } #ifndef TARGET_MLDUMP if (debugDump) out << " dump\n"; diff --git a/lib/app.h b/lib/app.h index fdac0f9..7a3a2d7 100644 --- a/lib/app.h +++ b/lib/app.h @@ -28,12 +28,20 @@ class AppEnv { CC_URL, CC_NOCACHE, } cacheControl; + enum { + FOPT_NONE, + FOPT_DENY, + FOPT_SAMEORIGIN, + FOPT_ALLOWFROM /* ブラウザに実装されていない */ + } frameOpt; + ustring foptUri; bool debugDump; AppEnv () { cacheControl = CC_NONE; postlimit = cPOSTLIMITDEFAULT; postfilelimit = cPOSTFILELIMITDEFAULT; + frameOpt = FOPT_NONE; debugDump = false; }; virtual ~AppEnv () {}; diff --git a/lib/http.cc b/lib/http.cc index ea2468e..b447971 100644 --- a/lib/http.cc +++ b/lib/http.cc @@ -133,28 +133,14 @@ void HTTPResponse::setRandomCookie (MotorEnv* env) { setCookie (uR, u, uSlash, 0, 0, uEmpty, false, env); } -void HTTPResponse::standardResponse (MotorOutput* out, const ustring& type, const ustring& charset, MotorEnv* env, MNode* header) { +//void HTTPResponse::standardResponse (MotorOutput* out, const ustring& type, const ustring& charset, MotorEnv* env, MNode* header) { +void HTTPResponse::standardResponse (MotorOutput* out, const ustring& type, const ustring& charset, MotorEnv* env) { if (! charset.empty () && matchHead (type, CharConst ("text/"))) { out->out_raw (CharConst (kRES_TYPE))->out_noCtrl (type)->out_raw (CharConst ("; " kCHARSET))->out_noCtrl (charset)->out_raw (uLF); } else { out->out_raw (CharConst (kRES_TYPE))->out_noCtrl (type)->out_raw (uLF); } - if (header) { - MNode* a; - static uregex reName ("[\\x00- :\\x7f-\\xff]+"); - static uregex reBody ("[\\x00- \\x7f-\\xff]+"); - // Header名前にコロンを含めることはできない。 - // Headerの名前,値に制御文字や多バイト文字を含めない。 - while (header && header->isCons ()) { - if (a = header->car ()) { - ustring u = to_string (a->car ()); - if (u.length () > 0) { - out->out_raw (omitPattern (to_string (a->car ()), reName))->out_raw (CharConst (": "))->out_raw (omitPattern (to_string (a->cdr ()), reBody))->out_raw (uLF); - } - } - nextNode (header); - } - } + printMoreHeader (out, env); #ifdef HTTPS_NOCACHE if (fNoCache || isHTTPS ()) printNoCache (out); @@ -166,6 +152,23 @@ void HTTPResponse::standardResponse (MotorOutput* out, const ustring& type, con #ifdef NO_KEEPALIVE out->out_raw (CharConst (kRES_CONNECTION_CLOSE))->out_raw (uLF); #endif + switch (frameOpt) { + case FOPT_NONE: + break; + case FOPT_DENY: + out->out_raw (CharConst (kRES_FRAMEOPT "DENY"))->out_raw (uLF); + break; + case FOPT_SAMEORIGIN: + out->out_raw (CharConst (kRES_FRAMEOPT "SAMEORIGIN"))->out_raw (uLF); + break; + case FOPT_ALLOWFROM: + assert (0); + out->out_raw (CharConst (kRES_FRAMEOPT "ALLOW-FROM ")); + out->out_toHTML (foptUri); + out->out_raw (uLF); + break; + default:; + } out->out_raw (uLF); } @@ -174,6 +177,7 @@ void HTTPResponse::standardResponse_html (MotorOutput* out, MotorEnv* env) { if (env && env->output) { out->out_raw (CharConst ("; " kCHARSET))->out_noCtrl (env->output->charset ())->out_raw (uLF); } + printMoreHeader (out, env); #ifdef HTTPS_NOCACHE if (fNoCache || isHTTPS ()) printNoCache (out); @@ -287,6 +291,29 @@ void HTTPResponse::forbiddenResponse (MotorOutput* out, MotorEnv* env) { "Forbidden.\n")); } +void HTTPResponse::setHeader (const ustring& key, const ustring& val) { + umatch m; + static uregex re ("^[a-zA-Z_0-9-]+$"); + + if (!usearch (key, m, re)) + throw (key + ": bad header name."); + if (!checkASCII (val)) + throw (val + ": bad header value."); + moreheader.push_back (std::pair (key, val)); +} + +void HTTPResponse::printMoreHeader (MotorOutput* out, MotorEnv* env) { + std::vector >::iterator it; + for (it = moreheader.begin (); it != moreheader.end (); ++ it) { + out->out_raw ((*it).first)->out_raw (CharConst (": "))->out_raw ((*it).second)->out_raw (uLF); +#ifdef DEBUG + if (env->log) { + *env->log << (*it).first << ": " << (*it).second << "\n"; + } +#endif /* DEBUG */ + } +} + #if 0 void HTTPSend::setMethod (const ustring& _method, bool _fpost) { static uregex re ("^(GET|HEAD|POST|OPTIONS|PUT|DELETE|TRACE|PATCH|LINK|UNLINK)$"); diff --git a/lib/http.h b/lib/http.h index 327b987..a9d43f7 100644 --- a/lib/http.h +++ b/lib/http.h @@ -19,8 +19,16 @@ class HTTPResponse { ustring cookie; boost::unordered_map cookiemap; bool fNoCache; + std::vector > moreheader; + enum { + FOPT_NONE, + FOPT_DENY, + FOPT_SAMEORIGIN, + FOPT_ALLOWFROM /* ブラウザに実装されていない */ + } frameOpt; + ustring foptUri; /* */ - HTTPResponse (): cookieDone (false), fNoCache (false) {}; + HTTPResponse (): cookieDone (false), fNoCache (false), frameOpt (FOPT_NONE) {}; virtual ~HTTPResponse () {}; virtual void printNoCache (MotorOutput* out); virtual void printCookie (MotorOutput* out, MotorEnv* env); @@ -29,13 +37,15 @@ class HTTPResponse { virtual void parseCookie (); virtual ustring readCookie (const ustring& key); virtual void setRandomCookie (MotorEnv* env); - virtual void standardResponse (MotorOutput* out, const ustring& type, const ustring& charset, MotorEnv* env, MNode* header = NULL); + virtual void standardResponse (MotorOutput* out, const ustring& type, const ustring& charset, MotorEnv* env); virtual void standardResponse_html (MotorOutput* out, MotorEnv* env); virtual void disposition (MotorOutput* out, bool finline, const ustring& name); virtual void location (MotorOutput* out, const ustring& url, MotorEnv* env); virtual void location_html (MotorOutput* out, const ustring& url, MotorEnv* env); virtual void noContentResponse (MotorOutput* out, MotorEnv* env); virtual void forbiddenResponse (MotorOutput* out, MotorEnv* env); + virtual void setHeader (const ustring& key, const ustring& val); + virtual void printMoreHeader (MotorOutput* out, MotorEnv* env); }; class CookieInfo { diff --git a/lib/httpconst.h b/lib/httpconst.h index f5b3ad6..f4076d0 100644 --- a/lib/httpconst.h +++ b/lib/httpconst.h @@ -63,6 +63,7 @@ #define kRES_TYPE "Content-type: " #define kRES_DISP "Content-Disposition: " #define kRES_CONNECTION_CLOSE "Connection: close" +#define kRES_FRAMEOPT "X-Frame-Options: " #define kCHARSET "charset=" #define kATTACHMENT "attachment" #define kINLINE "inline" diff --git a/lib/motorenv.cc b/lib/motorenv.cc index 14f733c..7f41528 100644 --- a/lib/motorenv.cc +++ b/lib/motorenv.cc @@ -318,6 +318,7 @@ void MotorEnv::setDefault () { } errorHtmlFile = appenv->errorHtml; mimetype = appenv->mimetype; + setFrameOpt (); } void MotorEnv::setDefaultDatastore () { @@ -334,6 +335,25 @@ void MotorEnv::setDatastore (const ustring& name) { } } +void MotorEnv::setFrameOpt () { + switch (appenv->frameOpt) { + case AppEnv::FOPT_NONE: + http.frameOpt = HTTPResponse::FOPT_NONE; + break; + case AppEnv::FOPT_DENY: + http.frameOpt = HTTPResponse::FOPT_DENY; + break; + case AppEnv::FOPT_SAMEORIGIN: + http.frameOpt = HTTPResponse::FOPT_SAMEORIGIN; + break; + case AppEnv::FOPT_ALLOWFROM: + http.frameOpt = HTTPResponse::FOPT_ALLOWFROM; + http.foptUri = appenv->foptUri; + break; + default:; + } +} + void MotorEnv::readFormVar () { if (form->contentType == CGIForm::T_XML) { form->read_raw (appenv->postlimit); @@ -509,16 +529,16 @@ void MotorEnv::standardResponse (const ustring& type) { responseDone = true; } -void MotorEnv::standardResponse (const ustring& type, const ustring& charset, const ustring& dispname, bool finline, MNode* header) { +void MotorEnv::standardResponse (const ustring& type, const ustring& charset, const ustring& dispname, bool finline) { if (responseDone) return; if (dispname.length () > 0) { http.disposition (output, finline, dispname); - http.standardResponse (output, type, output->charset (), this, header); + http.standardResponse (output, type, output->charset (), this); } else if (matchHead (type, CharConst ("text/")) && charset.length () > 0) { - http.standardResponse (output, type, charset, this, header); + http.standardResponse (output, type, charset, this); } else { - http.standardResponse (output, type, output->charset (), this, header); + http.standardResponse (output, type, output->charset (), this); } responseDone = true; } diff --git a/lib/motorenv.h b/lib/motorenv.h index 710cc95..5c56e50 100644 --- a/lib/motorenv.h +++ b/lib/motorenv.h @@ -76,6 +76,7 @@ class MotorEnv { virtual void setDefault (); virtual void setDefaultDatastore (); virtual void setDatastore (const ustring& name); + virtual void setFrameOpt (); virtual void readFormVar (); virtual void cacheControl (); virtual void doML (); @@ -92,7 +93,7 @@ class MotorEnv { standardResponse (mimetype); }; virtual void standardResponse (const ustring& type); - virtual void standardResponse (const ustring& type, const ustring& charset, const ustring& dispname, bool finline, MNode* header = NULL); + virtual void standardResponse (const ustring& type, const ustring& charset, const ustring& dispname, bool finline); virtual void standardResponse_html (); virtual void noContentResponse (); virtual void forbiddenResponse (); diff --git a/modules/ml-apache.cc b/modules/ml-apache.cc index 0c763a7..e9ea3b0 100644 --- a/modules/ml-apache.cc +++ b/modules/ml-apache.cc @@ -304,57 +304,3 @@ MNode* ml_read_cookie (MNode* cell, MlEnv* mlenv) { return newMNode_str (new ustring (val)); } -/*DOC: -===set-cookie=== - (set-cookie NAME VALUE [:path PATH] [:domain DOMAIN] [:span TIME] [:limit TIME]) -> NIL - -*/ -//#AFUNC set-cookie ml_set_cookie -MNode* ml_set_cookie (MNode* cell, MlEnv* mlenv) { - MNode* arg = cell->cdr (); - ustring name; - ustring value; - ustring path; - ustring domain; - time_t span = 0; - time_t limit = 0; - bool fsecure = false; - std::vector params; - std::vector keywords; - static paramList kwlist[] = { - {CharConst ("path"), false}, - {CharConst ("domain"), false}, - {CharConst ("span"), false}, - {CharConst ("limit"), false}, - {CharConst ("secure"), true}, - {NULL, 0, 0} - }; - - setParams (arg, 2, ¶ms, kwlist, &keywords, NULL); - name = eval_asciiword (params[0], mlenv); - value = eval_str (params[1], mlenv); - if (keywords[0]) - path = eval_asciiword (keywords[0], mlenv); - if (keywords[1]) - domain = eval_asciiword (keywords[1], mlenv); - if (keywords[2]) { - span = eval_int (keywords[2], mlenv); - if (span < 0) - span = 0; - } - if (keywords[3]) { - limit = eval_int (keywords[3], mlenv); - if (limit < 0) - limit = 0; - } - if (keywords[4]) - fsecure = eval_bool (keywords[4], mlenv); - if (name.length () > 128) - throw (ustring (CharConst ("too long name."))); - if (value.length () > 512) - throw (ustring (CharConst ("too long value."))); - - mlenv->env->http.setCookie (name, value, path, span, limit, domain, fsecure, mlenv->env); - - return NULL; -} diff --git a/modules/ml-apache.h b/modules/ml-apache.h index cea1870..6ea8f89 100644 --- a/modules/ml-apache.h +++ b/modules/ml-apache.h @@ -23,6 +23,5 @@ MNode* ml_is_get_method (MNode* cell, MlEnv* mlenv); MNode* ml_is_post_method (MNode* cell, MlEnv* mlenv); MNode* ml_absolute_url (MNode* cell, MlEnv* mlenv); MNode* ml_read_cookie (MNode* cell, MlEnv* mlenv); -MNode* ml_set_cookie (MNode* cell, MlEnv* mlenv); #endif /* ML_APACHE_H */ diff --git a/modules/ml-motor.cc b/modules/ml-motor.cc index 9a279ea..fe39d9c 100644 --- a/modules/ml-motor.cc +++ b/modules/ml-motor.cc @@ -19,7 +19,7 @@ /*DOC: ===output-header=== - (output-header TYPE [#inline | :inline BOOL] [:name NAME] [:charset NAME] [:header '((NAME . VALUE)...)]) -> NIL + (output-header TYPE [#inline | :inline BOOL] [:name NAME] [:charset NAME]) -> NIL :dispositionと:charsetは,排他。 */ @@ -28,7 +28,6 @@ MNode* ml_output_header (MNode* cell, MlEnv* mlenv) { MNode* arg = cell->cdr (); ustring type; bool finline = false; - MNodePtr header; ustring name; ustring charset; std::vector params; @@ -37,7 +36,6 @@ MNode* ml_output_header (MNode* cell, MlEnv* mlenv) { {CharConst ("inline"), true}, // 0 {CharConst ("name"), false}, // 1 {CharConst ("charset"), false}, // 2 - {CharConst ("header"), false}, // 3 {NULL, 0, 0} }; @@ -49,8 +47,6 @@ MNode* ml_output_header (MNode* cell, MlEnv* mlenv) { name = eval_text1 (keywords[1], mlenv); if (keywords[2]) charset = eval_asciiword (keywords[2], mlenv); - if (keywords[3]) - header = eval (keywords[3], mlenv); if (type.empty ()) throw (ustring (CharConst ("missing type."))); @@ -58,7 +54,7 @@ MNode* ml_output_header (MNode* cell, MlEnv* mlenv) { type = mimetype (type); if (! mlenv->env->responseDone) { - mlenv->env->standardResponse (type, charset, name, finline, header ()); + mlenv->env->standardResponse (type, charset, name, finline); } return NULL; @@ -277,3 +273,83 @@ MNode* ml_motor_output_string (MNode* cell, MlEnv* mlenv) { return newMNode_str (new ustring (out.ans)); } + +/*DOC: +===set-cookie=== + (set-cookie NAME VALUE [:path PATH] [:domain DOMAIN] [:span TIME] [:limit TIME]) -> NIL + +*/ +//#AFUNC set-cookie ml_set_cookie +MNode* ml_set_cookie (MNode* cell, MlEnv* mlenv) { + MNode* arg = cell->cdr (); + ustring name; + ustring value; + ustring path; + ustring domain; + time_t span = 0; + time_t limit = 0; + bool fsecure = false; + std::vector params; + std::vector keywords; + static paramList kwlist[] = { + {CharConst ("path"), false}, + {CharConst ("domain"), false}, + {CharConst ("span"), false}, + {CharConst ("limit"), false}, + {CharConst ("secure"), true}, + {NULL, 0, 0} + }; + + setParams (arg, 2, ¶ms, kwlist, &keywords, NULL); + name = eval_asciiword (params[0], mlenv); + value = eval_str (params[1], mlenv); + if (keywords[0]) + path = eval_asciiword (keywords[0], mlenv); + if (keywords[1]) + domain = eval_asciiword (keywords[1], mlenv); + if (keywords[2]) { + span = eval_int (keywords[2], mlenv); + if (span < 0) + span = 0; + } + if (keywords[3]) { + limit = eval_int (keywords[3], mlenv); + if (limit < 0) + limit = 0; + } + if (keywords[4]) + fsecure = eval_bool (keywords[4], mlenv); + if (name.length () > 128) + throw (ustring (CharConst ("too long name."))); + if (value.length () > 512) + throw (ustring (CharConst ("too long value."))); + + mlenv->env->http.setCookie (name, value, path, span, limit, domain, fsecure, mlenv->env); + + return NULL; +} + +/*DOC: +===set-header=== + (set-header NAME VALUE ...) -> NIL + +*/ +//#AFUNC set-header ml_set_header +MNode* ml_set_header (MNode* cell, MlEnv* mlenv) { + MNode* arg = cell->cdr (); + ustring name; + ustring value; + + while (arg) { + name = eval_str (arg->car (), mlenv); + nextNode (arg); + if (arg) + value = eval_str (arg->car (), mlenv); + else + value.resize (0); + nextNode (arg); + mlenv->env->http.setHeader (name, value); + } + + return NULL; +} diff --git a/modules/ml-motor.h b/modules/ml-motor.h index 096ad88..fcffffa 100644 --- a/modules/ml-motor.h +++ b/modules/ml-motor.h @@ -11,5 +11,7 @@ MNode* ml_motor_output (MNode* cell, MlEnv* mlenv); MNode* ml_motor_output_html (MNode* cell, MlEnv* mlenv); MNode* ml_motor_output_raw (MNode* cell, MlEnv* mlenv); MNode* ml_motor_output_string (MNode* cell, MlEnv* mlenv); +MNode* ml_set_cookie (MNode* cell, MlEnv* mlenv); +MNode* ml_set_header (MNode* cell, MlEnv* mlenv); #endif /* ML_MOTOR_H */ -- 2.11.0