-rw-r--r-- | src/.gitignore | 7 | ||||
-rw-r--r-- | src/Makefile.am | 15 | ||||
-rw-r--r-- | src/cgi_gateway.cc | 88 | ||||
-rw-r--r-- | src/cgi_interface.cc | 16 | ||||
-rw-r--r-- | src/fastcgi.cc | 67 | ||||
-rw-r--r-- | src/util.cc | 53 |
6 files changed, 246 insertions, 0 deletions
diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..af4f4dd --- a/dev/null +++ b/src/.gitignore | |||
@@ -0,0 +1,7 @@ | |||
1 | Makefile.in | ||
2 | .deps | ||
3 | Makefile | ||
4 | .libs | ||
5 | *.o | ||
6 | *.lo | ||
7 | *.la | ||
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..8a5447b --- a/dev/null +++ b/src/Makefile.am | |||
@@ -0,0 +1,15 @@ | |||
1 | lib_LTLIBRARIES = libkingate.la libkingate-fcgi.la | ||
2 | |||
3 | INCLUDES = -I${top_srcdir}/include | ||
4 | AM_CXXFLAGS = ${KONFORKA_CFLAGS} | ||
5 | LDADD = ${KONFORKA_LIBS} | ||
6 | |||
7 | libkingate_la_SOURCES = \ | ||
8 | cgi_gateway.cc \ | ||
9 | cgi_interface.cc \ | ||
10 | util.cc | ||
11 | libkingate_la_LDFLAGS = -version-info 1:0:0 | ||
12 | |||
13 | libkingate_fcgi_la_SOURCES = \ | ||
14 | fastcgi.cc | ||
15 | libkingate_fcgi_la_LDFLAGS = -version-info 1:0:0 | ||
diff --git a/src/cgi_gateway.cc b/src/cgi_gateway.cc new file mode 100644 index 0000000..eae7a03 --- a/dev/null +++ b/src/cgi_gateway.cc | |||
@@ -0,0 +1,88 @@ | |||
1 | #include "kingate/cgi_gateway.h" | ||
2 | #include "kingate/util.h" | ||
3 | #include "kingate/exception.h" | ||
4 | |||
5 | namespace kingate { | ||
6 | |||
7 | cgi_gateway::cgi_gateway(cgi_interface& ci) | ||
8 | : iface(ci), b_parsed_content(false) { | ||
9 | // Fetch GET content | ||
10 | if(iface.has_meta("QUERY_STRING")) { | ||
11 | string qs = iface.get_meta("QUERY_STRING"); | ||
12 | parse_query(qs,get); | ||
13 | } | ||
14 | // Fetch POST content | ||
15 | if(!strcasecmp(get_content_type().c_str(),"application/x-www-form-urlencoded")) { | ||
16 | unsigned long cl = get_content_length(); | ||
17 | if(cl) { | ||
18 | char * tmp = new char[cl]; | ||
19 | iface.in().read(tmp,cl); | ||
20 | string qs(tmp,cl); | ||
21 | delete tmp; | ||
22 | parse_query(qs,post); | ||
23 | } | ||
24 | b_parsed_content = true; | ||
25 | } | ||
26 | } | ||
27 | |||
28 | bool cgi_gateway::has_GET(const string& n) const { | ||
29 | return get.find(n) != get.end(); | ||
30 | } | ||
31 | string cgi_gateway::get_GET(const string& n) const { | ||
32 | params_t::const_iterator i = get.find(n); | ||
33 | if(i==get.end()) | ||
34 | throw exception_notfound(CODEPOINT,"no such parameter"); | ||
35 | return i->second; | ||
36 | } | ||
37 | bool cgi_gateway::has_POST(const string& n) const { | ||
38 | return post.find(n) != post.end(); | ||
39 | } | ||
40 | string cgi_gateway::get_POST(const string& n) const { | ||
41 | params_t::const_iterator i = post.find(n); | ||
42 | if(i==post.end()) | ||
43 | throw exception_notfound(CODEPOINT,"no such parameter"); | ||
44 | return i->second; | ||
45 | } | ||
46 | bool cgi_gateway::has_param(const string& n) const { | ||
47 | return has_GET(n) || has_POST(n); | ||
48 | } | ||
49 | string cgi_gateway::get_param(const string& n) const { | ||
50 | params_t::const_iterator i = get.find(n); | ||
51 | if(i!=get.end()) | ||
52 | return i->second; | ||
53 | i = post.find(n); | ||
54 | if(i!=post.end()) | ||
55 | return i->second; | ||
56 | throw exception_notfound(CODEPOINT,"no such parameter"); | ||
57 | } | ||
58 | |||
59 | const string& cgi_gateway::get_content_type() const { | ||
60 | if(!has_meta("CONTENT_TYPE")) | ||
61 | return ""; // XXX: | ||
62 | return get_meta("CONTENT_TYPE"); | ||
63 | } | ||
64 | unsigned long cgi_gateway::get_content_length() const { | ||
65 | if(!has_meta("CONTENT_LENGTH")) | ||
66 | return 0; | ||
67 | string cl = get_meta("CONTENT_LENGTH"); | ||
68 | return strtol(cl.c_str(),NULL,10); | ||
69 | } | ||
70 | |||
71 | void cgi_gateway::parse_query(string& q,params_t& p) { | ||
72 | while(!q.empty()) { | ||
73 | string::size_type amp = q.find('&'); | ||
74 | string pp = (amp==string::npos)?q:q.substr(0,amp); | ||
75 | if(amp==string::npos) | ||
76 | q.clear(); | ||
77 | else | ||
78 | q.erase(0,amp+1); | ||
79 | string::size_type eq = pp.find('='); | ||
80 | if(eq == string::npos) { | ||
81 | p.insert(params_t::value_type("",url_unescape(pp))); | ||
82 | }else{ | ||
83 | p.insert(params_t::value_type(url_unescape(pp.substr(0,eq)),url_unescape(pp.substr(eq+1)))); | ||
84 | } | ||
85 | } | ||
86 | } | ||
87 | |||
88 | } | ||
diff --git a/src/cgi_interface.cc b/src/cgi_interface.cc new file mode 100644 index 0000000..ffbd2bf --- a/dev/null +++ b/src/cgi_interface.cc | |||
@@ -0,0 +1,16 @@ | |||
1 | #include "kingate/cgi_interface.h" | ||
2 | #include "kingate/exception.h" | ||
3 | |||
4 | namespace kingate { | ||
5 | |||
6 | bool cgi_interface::has_meta(const string& n) const { | ||
7 | return metavars.find(n) != metavars.end(); | ||
8 | } | ||
9 | const string& cgi_interface::get_meta(const string& n) const { | ||
10 | metavars_t::const_iterator rv = metavars.find(n); | ||
11 | if(rv == metavars.end()) | ||
12 | throw exception_notfound(CODEPOINT,"no such meta-variable"); | ||
13 | return rv->second; | ||
14 | } | ||
15 | |||
16 | } | ||
diff --git a/src/fastcgi.cc b/src/fastcgi.cc new file mode 100644 index 0000000..7484449 --- a/dev/null +++ b/src/fastcgi.cc | |||
@@ -0,0 +1,67 @@ | |||
1 | #include <unistd.h> | ||
2 | #include <sys/types.h> | ||
3 | #include <sys/stat.h> | ||
4 | #include "kingate/fastcgi.h" | ||
5 | #include "kingate/exception.h" | ||
6 | |||
7 | namespace kingate { | ||
8 | |||
9 | bool fcgi_socket::_initialized = false; | ||
10 | |||
11 | fcgi_socket::fcgi_socket(const char *s,int bl) | ||
12 | : sock(-1) { | ||
13 | if(!_initialized) { | ||
14 | if( FCGX_Init() ) | ||
15 | throw exception(CODEPOINT,"failed to FCGX_Init()"); | ||
16 | _initialized = true; | ||
17 | } | ||
18 | sock = FCGX_OpenSocket(s,bl); | ||
19 | if(sock<0) | ||
20 | throw exception(CODEPOINT,"failed to FCGX_OpenSocket("); | ||
21 | // TODO: check if there is a ':', not if it starts with ':' | ||
22 | if(*s != ':') | ||
23 | if(chmod(s,0777)) // XXX: configurable. | ||
24 | throw exception(CODEPOINT,"failed to chmod()"); | ||
25 | } | ||
26 | fcgi_socket::fcgi_socket(int s) | ||
27 | : sock(0) { | ||
28 | if(!_initialized) { | ||
29 | if( FCGX_Init() ) | ||
30 | throw exception(CODEPOINT,"failed to FCGX_Init()"); | ||
31 | _initialized = true; | ||
32 | } | ||
33 | } | ||
34 | fcgi_socket::~fcgi_socket() { | ||
35 | if(sock>=0) | ||
36 | close(sock); | ||
37 | } | ||
38 | |||
39 | fcgi_interface::fcgi_interface(fcgi_socket& s,int f) | ||
40 | : sin(&sbin), sout(&sbout), serr(&sberr) { | ||
41 | if( FCGX_InitRequest(&request,s.sock,f) ) | ||
42 | throw exception(CODEPOINT,"failed to FCGX_InitRequest()"); | ||
43 | if( FCGX_Accept_r(&request) ) | ||
44 | throw exception(CODEPOINT,"failed to FCGX_Accept_r()"); | ||
45 | sbin.attach(request.in); | ||
46 | sbout.attach(request.out); | ||
47 | sberr.attach(request.err); | ||
48 | metavars.clear(); // XXX: redundant. | ||
49 | for(char **p = request.envp; *p; p++) { | ||
50 | const char *e = strchr(*p,'='); | ||
51 | if(!e){ | ||
52 | // XXX: check if we have it already? | ||
53 | metavars[*p] = string(0); | ||
54 | }else{ | ||
55 | int l = e-*p; e++; | ||
56 | // XXX: check if we have it already? | ||
57 | metavars[string(*p,l)]=e; | ||
58 | } | ||
59 | } | ||
60 | } | ||
61 | fcgi_interface::~fcgi_interface() { | ||
62 | sout.flush(); | ||
63 | serr.flush(); | ||
64 | FCGX_Finish_r(&request); | ||
65 | } | ||
66 | |||
67 | } | ||
diff --git a/src/util.cc b/src/util.cc new file mode 100644 index 0000000..2e2d305 --- a/dev/null +++ b/src/util.cc | |||
@@ -0,0 +1,53 @@ | |||
1 | #include "kingate/util.h" | ||
2 | #include "kingate/exception.h" | ||
3 | |||
4 | namespace kingate { | ||
5 | |||
6 | static const char *safeChars = | ||
7 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||
8 | "abcdefghijklmnopqrstuvwxyz" | ||
9 | "0123456789" | ||
10 | "_-" ; | ||
11 | |||
12 | string url_escape(const string& str) { | ||
13 | string rv = str; | ||
14 | string::size_type screwed = 0; | ||
15 | for(;;) { | ||
16 | screwed = rv.find_first_not_of(safeChars,screwed); | ||
17 | if(screwed == string::npos) | ||
18 | break; | ||
19 | while(screwed<rv.length() && !strchr(safeChars,rv.at(screwed))) { | ||
20 | char danger = rv.at(screwed); | ||
21 | if(danger==' ') { | ||
22 | rv.replace(screwed++,1,1,'+'); | ||
23 | }else{ | ||
24 | static char tmp[4] = {'%',0,0,0}; | ||
25 | snprintf(&tmp[1],3,"%02X",0xFF&(int)danger); | ||
26 | rv.replace(screwed,1,tmp,3); | ||
27 | screwed+=3; | ||
28 | } | ||
29 | } | ||
30 | } | ||
31 | return rv; | ||
32 | } | ||
33 | string url_unescape(const string& str) { | ||
34 | string rv = str; | ||
35 | string::size_type unscrewed = 0; | ||
36 | for(;;) { | ||
37 | unscrewed = rv.find_first_of("%+",unscrewed); | ||
38 | if(unscrewed == string::npos) | ||
39 | break; | ||
40 | if(rv.at(unscrewed)=='+') { | ||
41 | rv.replace(unscrewed++,1,1,' '); | ||
42 | }else{ | ||
43 | if((rv.length()-unscrewed)<3) | ||
44 | throw exception(CODEPOINT,"incorrectly escaped string"); | ||
45 | // XXX: ensure it's hex? | ||
46 | int danger = strtol(rv.substr(unscrewed+1,2).c_str(),NULL,16); | ||
47 | rv.replace(unscrewed,3,1,danger); | ||
48 | unscrewed++; | ||
49 | } | ||
50 | } | ||
51 | return rv; | ||
52 | } | ||
53 | } | ||