fix jolie fuckups.

Signed-off-by: Luca Fulchir <luca@fulchir.it>
parent cdb33509
......@@ -9,17 +9,16 @@ SRC=${CWD}/jolie/net/
all: java install
java: ${SRC}/utils/Hmac.java ${SRC}/utils/HTTP.java ${SRC}/utils/HTTPServer.java
java: ${SRC}/utils/Hmac.java ${SRC}/utils/HTTP.java
- mkdir -p ${BUILDDIR} 2>/dev/null
${JAVAC} -cp ${JOLIEDIR}/jolie.jar:${JARDIR}/commons-codec-1.6.jar -d ${BUILDDIR} jolie/net/utils/Hmac.java
${JAVAC} -cp ${JOLIEDIR}/jolie.jar:${JARDIR}/httpcore-4.2.2.jar:${JARDIR}/httpclient-4.2.3.jar:${JARDIR}/commons-io-2.4.jar:${JARDIR}/commons-codec-1.6.jar:${JOLIEDIR}/extensions/http.jar:${JOLIEDIR}/lib/json_simple.jar -d ${BUILDDIR} jolie/net/utils/HTTP.java
${JAVAC} -cp ${JOLIEDIR}/jolie.jar:${JARDIR}/httpcore-4.2.2.jar:${JARDIR}/httpclient-4.2.3.jar:${JARDIR}/commons-io-2.4.jar:${JARDIR}/commons-codec-1.6.jar:${JOLIEDIR}/extensions/http.jar -d ${BUILDDIR} jolie/net/utils/HTTPServer.java
clean:
- rm -rf ${BUILDDIR}
install: ${BUILDDIR}/jolie/net/utils/Hmac.class ${BUILDDIR}/jolie/net/utils/HTTP.class ${BUILDDIR}/jolie/net/utils/HTTPServer.class
install: ${BUILDDIR}/jolie/net/utils/Hmac.class ${BUILDDIR}/jolie/net/utils/HTTP.class
cd ${BUILDDIR} && jar cvf ${PACKAGE_NAME} ./jolie/net/utils/*class && cd ${CWD}
cp ${BUILDDIR}/utils.jar ${JOLIEDIR}/javaServices/
cp ${JARDIR}/*.jar ${JOLIEDIR}/javaServices/
......
......@@ -50,13 +50,13 @@ scope (error_handler) {
http_data.URI = info.location_request;
toUpperCase@StringUtils (info.method) (http_data.method);
info.method = http_data.method;
http_data.headers[0].name = "userAgent";
http_data.headers[0].name = "User-Agent";
http_data.headers[0].value = "Jolie-OAuth";
http_data.headers[1].name = "Authorization";
DataRequestToken@OAuth1Data (info) (http_data.headers[1].value);
sendRequest@HTTP(http_data) (answer);
println@Console(answer)();
token << info;
parseAnswer@OAuth1Data(answer) (tmp);
tmp.search = "oauth_token";
......
......@@ -26,8 +26,9 @@ include "OAuth1Types.iol"
include "jolie/net/utils/Hmac.iol"
include "../utils/HTTPTypes.iol"
include "../utils/HTTP.iol"
include "console.iol"
execution {sequential}
execution {concurrent}
inputPort OAuth1Data{
Location: "local"
Interfaces: OAuth1DataInterface
......@@ -52,7 +53,7 @@ scope (error_handler) {
toSort.item[3] = "oauth_version=1.0";
toSort.item[4] = "oauth_consumer_key=" + info.consumer_key;
callback="oob";
toEncode.encoding = "UTF8";
toEncode.encoding = "UTF-8";
toEncode = info.callback;
URLencode@HTTP (toEncode) (callback);
toSort.item[5] = "oauth_callback=" + callback;
......@@ -64,7 +65,7 @@ scope (error_handler) {
sort@StringUtils (toSort) (sorted);
toEncode.encoding = "UTF8";
toEncode.encoding = "UTF-8";
toEncode = info.location_request;
URLencode@HTTP (toEncode) (encodedLocation);
......@@ -127,7 +128,7 @@ scope (error_handler) {
sort@StringUtils (toSort) (sorted);
toEncode.encoding = "UTF8";
toEncode.encoding = "UTF-8";
toEncode = info.location_access;
URLencode@HTTP (toEncode) (encodedLocation);
......@@ -205,27 +206,31 @@ scope (parsing) {
throw(wrong_header);
undef(header.prefix);
length@StringUtils(header)(len);
if (len >= 7)
if (len <= 7)
throw(wrong_header);
header.begin = 7;
header.begin = 6;
header.end = len;
substring@StringUtils(header)(answer);
undef (header.begin);
undef (header.end);
// soo... we don't have the 'while'? niiiice...
str = header;
str.regex=",";
split@StringUtils(str)(elements);
answer.regex=",";
split@StringUtils(answer)(elements);
for (count = 0, count < #elements.result, count++) {
toParse = elements.result[i];
toParse = elements.result[count];
toParse.regex = "=";
split@StringUtils(toParse)(nameval);
if (#nameval.result != 2)
throw(wrong_header);
pairs.pair[count].name = nameval.result[0];
pairs.pair[count].value = nameval.result[1]
if (#nameval.result == 2) {
str = nameval.result[1];
length@StringUtils(nameval.result[1])(str.end);
str.begin = 1;
str.end = str.end - 1;
substring@StringUtils(str)(str);
pairs.pair[count].value = str
} else {
pairs.pair[count].value = ""
}
}
}]{nullProcess}
......@@ -235,7 +240,7 @@ scope (parsing) {
for(i = 0, i < #pairs.pair, i++) {
if (pairs.pair[i].name != "oauth_signature") {
toSort.item[count] = pairs.pair[i].name + "=" +
pairs.pair[i].value;
pairs.pair[i].value ;
count++
} else {
old_signature = pairs.pair[i].value
......@@ -243,7 +248,7 @@ scope (parsing) {
};
sort@StringUtils (toSort) (sorted);
// ok, try to rebuild the string to verify the signature
toEncode.encoding = "UTF8";
toEncode.encoding = "UTF-8";
toEncode = pairs.location;
URLencode@HTTP (toEncode) (encodedLocation);
......@@ -256,17 +261,14 @@ scope (parsing) {
URLencode@HTTP (toEncode) (encodedParams);
// for an easy implementation, we don't check a database --
// we only accept one application. these are its secret and identifier
toHash.secret = pairs.secret;
toHash.data = pairs.method + "&" + encodedLocation + "&" +encodedParams;
sha1@Hmac (toHash) (tmpSignature);
toEncode = tmpSignature;
URLencode@HTTP (toEncode) (test_signature);
if (test_signature == pairs.signature) {
if (test_signature == pairs.signature)
boolRet = true
}
}]{nullProcess}
}
......@@ -55,10 +55,11 @@ type OAuth1_pairSearch:void {
}
type OAuth1Sig:void {
.pair :OAuth1_pair
.pair* :OAuth1_pair
.location :string
.method :string
.signature :string
.secret :string
}
interface OAuth1DataInterface {
......@@ -68,12 +69,12 @@ interface OAuth1DataInterface {
parseAnswer (string) (OAuth1_parsed) throws wrong_answer,
getPair (OAuth1_pairSearch) (string) throws not_found,
checkSignature (OAuth1Sig) (bool),
parseAuthHeader (string) (OAuth1Pair) throws wrong_header
parseAuthHeader (string) (OAuth1_parsed) throws wrong_header
}
interface OAuth1Interface {
RequestResponse:
requestToken (OAuth1Info) (OAuth1Info) throws wrong_answer,
getReferral (OAuth1Info) (string),
getAccess (OAuth1Info) (OAuth1Pair) throws wrong_answer
getAccess (OAuth1Info) (OAuth1_parsed) throws wrong_answer
}
......@@ -26,9 +26,11 @@ init
{
// data needed for the connection: where to connect, how and who we are
data.location_request = "https://api.dropbox.com/1/oauth/request_token";
data.location_request = "http://localhost:8080/tokenRequest";
data.location_request = "http://localhost:8080/authorize";
data.location_authorize = "https://www.dropbox.com/1/oauth/authorize";
data.location_authorize = "http://localhost:8080/authorize";
data.location_access = "https://api.dropbox.com/1/oauth/access_token";
data.location_access = "http://localhost:8080/access";
data.method = "POST";
data.consumer_key = "27250pnzil7tmhx";
data.secret = "6yj3c6mjbdixm6i";
......
......@@ -10,26 +10,30 @@ include "../utils/HTTP.iol"
execution{concurrent}
interface OAuth1Server {
interface OAuth1ServerIface {
RequestResponse:
OAuth1Authorize (HTTPCallbackReq) (HTTPCallbackAnsw),
OAuth1Authenticate (HTTPCallbackReq) (HTTPCallbackAnsw),
OAuth1Access (HTTPCallbackReq) (HTTPCallbackAnsw)
}
outputPort OAuth1Server {
Location: "local"
Interfaces: OAuth1ServerIface
}
inputPort OAuth1Server {
Location: "local"
Interfaces: OAuth1Server
Interfaces: OAuth1ServerIface
}
init
{
scope (errorHTTP) {
install (cannot_add =>
println@Console("cannot add page... wtf?")();
println@Console("Cannot add page...")();
throw (demo_failure),
cannot_listen =>
println@Console("Cannlot make HTTP server listen")();
println@Console("Cannot make HTTP server listen")();
throw (demo_failure)
);
// Create an HTTP Server, with identification "OAuth1" on following
......@@ -46,7 +50,7 @@ scope (errorHTTP) {
page.id = "OAuth1";
page = "/authorize";
page.operation = "OAuth1Authorize";
page.resource = "/";
page.resource = "OAuth1Server";
addPage@HTTP(page)(asd);
page = "/authenticate";
page.operation = "OAuth1Authenticate";
......@@ -74,35 +78,33 @@ main
* consumer_key = "27250pnzil7tmhx";
* secret = "6yj3c6mjbdixm6i";
*
* and an user with these credentials
* token =
*
* All other tokens will be random
*/
[OAuth1Authorize (input) (output) {
scope (earlyreturn) {
install (errreturn =>
output.code = int(500);
undef(output.headers);
undef(output.body),
output.body = "err 500",
wrong_signature =>
output.code = int(401); //unauthorized
undef(output.headers);
undef(output.body),
output.body = "err 401",
wrong_header =>
output.code = int(400); //wrong parameters
undef(output.headers);
undef(output.body),
output.body = "err 400",
missing_parameters =>
output.code = int(400); //wrong parameters
undef(output.headers);
undef(output.body)
output.body = "err 400"
);
if (input.page != "authorize") {
if (input.page != "/authorize") {
throw(errreturn)
};
search.pair = input.headers;
search.search = "Authentication";
search.pair << input.headers;
search.search = "Authorization";
pairSearch@HTTP (search) (answer);
if (!answer.found) {
throw(errreturn)
......@@ -115,12 +117,13 @@ scope (earlyreturn) {
search.pair << pairs.pair;
search.search = "oauth_consumer_key";
pairSearch@HTTP (search) (answer);
if (!answer.found)
if (!answer.found || answer != "27250pnzil7tmhx" ) //CHECK the user!
throw(missing_parameters);
search.search = "oauth_token";
search.search = "oauth_signature";
pairSearch@HTTP (search) (answer);
if (!answer.found)
throw(missing_parameters);
old_signature = answer;
search.search = "oauth_signature_method";
pairSearch@HTTP (search) (answer);
if (!answer.found || answer != "HMAC-SHA1") // only one supported
......@@ -138,28 +141,39 @@ scope (earlyreturn) {
pairSearch@HTTP (search) (answer);
if (!answer.found)
throw(missing_parameters);
undef (answer.found);
// everyone loves clock synchronization... 5 minutes here :)
// keep your clock synched, all good protocols require that... sure...
getCurrentTimeMillis@Time(void)(tmp_timestamp);
tmp_timestamp = tmp_timestamp / 1000;
if ((answer > (tmp_timestamp + 300)) ||(answer < (tmp_timestamp - 300)))
throw(missing_parameters);
timestamp = tmp_timestamp / 1000;
time_got = int(answer);
time_more = timestamp + 300;
time_less = timestamp - 300;
// ok... 2 hour of tests, and this is what I have to do... -.-''
tmp = string(time_more);
time_more = int(tmp);
tmp2 = string(time_less);
time_less = int(tmp2);
undef(tmp);
undef(tmp2);
if ((time_got > time_more) ||(time_got < time_less))
throw(wrong_header);
search.search = "oauth_version";
pairSearch@HTTP (search) (answer);
if (answer.found) {
if (answer != "1.0") // only this is supported by RFC
throw(missing_parameters)
};
search.search = "oauth_callback"; // MUST be defined.
pairSearch@HTTP (search) (answer);
if (!answer.found)
throw(missing_parameters);
last_callback = answer;
check.pair << search.pair;
check.location = "http://127.0.0.1:8080/authorize";
toUpperCase@StringUtils(info.method)(check.method);
check.location = "http://localhost:8080/authorize";
toUpperCase@StringUtils(input.method)(check.method);
check.signature = old_signature;
// secret & token_secret concat. we don't have the token_secret now.
// we checked the user id before, we use its secret now.
check.secret = "6yj3c6mjbdixm6i&";
checkSignature@OAuth1Data(check)(testsignature);
if (!testsignature)
......@@ -170,7 +184,7 @@ scope (earlyreturn) {
createSecureToken@SecurityUtils(void)(issued_token_secret);
output.code = "200";
output.code = 200;
// finally communicate the data
output.headers[0].name="Content-Type";
output.headers[0].value="application/x-www-form-urlencoded";
......@@ -195,7 +209,7 @@ scope (earlyreturn) {
undef(output.headers);
undef(output.body)
);
if (input.page != "authenticate") {
if (input.page != "/authenticate") {
throw(errreturn)
};
......@@ -246,16 +260,17 @@ scope (earlyreturn) {
undef(output.body)
);
scope (earlyreturn) {
if (input.page != "access") {
if (input.page != "/access") {
throw(errreturn)
};
search.pair = input.headers;
search.search = "Authentication";
search.pair << input.headers;
search.search = "Authorization";
pairSearch@HTTP (search) (answer);
if (!answer.found) {
throw(errreturn)
};
undef(answer.found);
parseAuthHeader@OAuth1Data(answer)(pairs);
......
......@@ -92,11 +92,11 @@ scope (earlyreturn) {
undef(output.headers);
undef(output.body)
);
if (input.page != "authorize") {
if (input.page != "/authorize") {
throw(errreturn)
};
search.pair = input.headers;
search.pair << input.headers;
search.search = "Authentication";
pairSearch@HTTP (search) (answer);
if (!answer.found) {
......@@ -208,7 +208,7 @@ scope (earlyreturn) {
undef(output.body)
);
scope (earlyreturn) {
if (input.page != "access") {
if (input.page != "/access") {
throw(errreturn)
};
......
......@@ -29,7 +29,6 @@ Interfaces:
embedded {
Java:
"jolie.net.utils.HTTP" in HTTP,
"jolie.net.utils.HTTPServer" in HTTP
"jolie.net.utils.HTTP" in HTTP
}
......@@ -21,6 +21,10 @@
package jolie.net.utils;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
......@@ -37,11 +41,19 @@ import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.apache.commons.io.IOUtils;
import org.apache.commons.codec.net.URLCodec;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.concurrent.Executors;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import jolie.net.CommMessage;
import jolie.runtime.JavaService;
import jolie.runtime.Value;
import jolie.runtime.ValueVector;
......@@ -315,4 +327,207 @@ private static String sendPost (HttpClient httpclient, Value req)
throw new FaultException("send_error", e);
}
}
/*
* Server - side HTTP
*
*/
class SrvInfo
{
public HttpServer server;
public boolean started;
public String jolieOperation;
public String jolieResource;
public String host;
public int port;
}
Map <String, SrvInfo> servers = new HashMap<String, SrvInfo>();;
@RequestResponse
public void initialize(Value data) throws FaultException {
synchronized(servers) {
SrvInfo info = new SrvInfo();
info.host = data.getFirstChild("host").strValue();
info.port = data.getFirstChild("port").intValue();
info.jolieOperation = data.getFirstChild("operation").strValue();
info.jolieResource = data.getFirstChild("resource").strValue();
info.started = false;
info.server = null;
try {
info.server = HttpServer.create(new InetSocketAddress(
info.host, info.port), 0);
} catch (Exception e) {
throw new FaultException("cannot_listen", e);
}
servers.put(data.getFirstChild("id").strValue(), info);
}
}
@RequestResponse
public void addPage(Value page) throws FaultException {
synchronized(servers) {
// if no such server
if (!servers.containsKey(page.getFirstChild("id").strValue()))
throw new FaultException("cannot_add");
SrvInfo srv = servers.get(page.getFirstChild("id").strValue());
// if server already started
if (srv.started)
throw new FaultException("cannot_add");
if (page.hasChildren("operation") && page.hasChildren("resource")) {
srv.server.createContext(page.strValue(),
new MyHandler(page.strValue(),
page.getFirstChild("operation").strValue(),
page.getFirstChild("resource").strValue()));
} else {
srv.server.createContext(page.strValue(),
new MyHandler(page.strValue(),
srv.jolieOperation, srv.jolieResource));
}
}
}
@RequestResponse
public void start(String id) throws FaultException {
synchronized(servers) {
if (!servers.containsKey(id))
throw new FaultException("no_server");
servers.get(id).server.setExecutor(Executors.newCachedThreadPool());
servers.get(id).server.start();
}
}
@RequestResponse
public Value pairSearch (Value search) throws FaultException {
String toFind = search.getFirstChild("search").strValue();
String ret = "";
boolean found = false;
for (Value child : search.getChildren("pair")) {
if (child.getFirstChild("name").strValue().equals(toFind)) {
found = true;
if (child.hasChildren("value")) {
ret = child.getFirstChild("value").strValue();
}
}
}
Value val = Value.create();
val.setValue(ret);
val.getNewChild("found").setValue(found);
return val;
}
class MyHandler implements HttpHandler
{
private String page;
private String operation;
private String resource;
public MyHandler() {
page = null;
operation = null;
resource = null;
}
public MyHandler(String page, String operation, String resource) {
this.page = page;
this.operation = operation;
this.resource = resource;
}
public void handle (HttpExchange xchg) throws IOException {
if (page == null || operation == null || resource == null)
throw new IOException("HTTP: missing callback");
Value req = Value.create();
// Add HTTP protocol of request
req.getNewChild("protocol").setValue(xchg.getProtocol());
req.getNewChild("method").setValue(xchg.getRequestMethod());
req.getNewChild("page").setValue(xchg.getRequestURI().getPath());
String query = xchg.getRequestURI().getQuery();
// save queries parameters as name-value entries
if (query != null) {
String[] queries = query.split("&");
for (int i = 0; i < queries.length; i++) {
String[] nameval = queries[i].split("=");
Value toAdd = Value.create();
if (nameval.length == 1) {
toAdd.getNewChild("name").setValue(nameval[0]);
toAdd.getNewChild("value").setValue("");
req.getNewChild("query").deepCopy(toAdd);
} else if (nameval.length == 2) {
toAdd.getNewChild("name").setValue(nameval[0]);
toAdd.getNewChild("value").setValue(nameval[1]);
req.getNewChild("query").deepCopy(toAdd);
}
}
}
String encoding = "UTF-8";
Headers headers = xchg.getRequestHeaders();
Set<Map.Entry<String, List<String>>> entries = headers.entrySet();
for (Map.Entry<String, List<String>> entry : entries) {
if (entry.getKey().toLowerCase().equals("encoding"))
encoding = entry.getValue().toString();
Value pair = Value.create();
pair.getNewChild("name").setValue(entry.getKey());
pair.getNewChild("value").setValue(entry.getValue().get(0)
.toString());
req.getNewChild("headers").deepCopy(pair);
System.out.println("header:" + entry.getKey() + "=>" + entry.getValue().get(0).toString());
}
StringWriter writer = new StringWriter();
IOUtils.copy(xchg.getRequestBody(), writer, encoding);
req.getNewChild("body").setValue(writer.toString());
CommMessage resp = null;
try {
// send the request to the proper operation/resource
CommMessage request = CommMessage.createRequest(operation,
resource, req);
// get answer
resp = sendMessage(request).recvResponseFor(request);
} catch( Exception e ){
throw new IOException("HTTP can't send", e);
}
// send back answer
Value response = resp.value();
StringBuffer output = new StringBuffer();
// check for needed parameters
if (!response.hasChildren("code"))
throw new IOException("Wrong MessagetType to HTTP");
int HTTPCode;
try {
HTTPCode = response.getFirstChild("code").intValue();
} catch (Exception e) {
throw new IOException("Wrong MessageType to HTTP", e);
}
if (response.hasChildren("headers")) {
//add headers to our response
ValueVector children = response.getChildren("headers");
for(int i = 0; i < children.size(); i++) {
Value pair = children.get(i);
if (!pair.hasChildren("name") ||
!pair.hasChildren("value")) {
throw new IOException(
"Wrong MessageType to HTTP");
}
Headers h = xchg.getResponseHeaders();
h.add(pair.getFirstChild("name").strValue(),
pair.getFirstChild("value").strValue());
}
}
if (response.hasChildren("body")) {
output.append(response.getFirstChild("body").strValue());
}
xchg.sendResponseHeaders(HTTPCode, output.length());
OutputStream os = xchg.getResponseBody();
os.write(output.toString().getBytes());
os.close();
}
}
}
/* *************************************************************************
* Copyright (C) 2013 by Luca Fulchir <luca@fulchir.it> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Library General Public License as *
* published by the Free Software Foundation; either version 2 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* For details about the authors of this software, see the AUTHORS file. *
***************************************************************************/
package jolie.net.utils;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.io.StringWriter;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import jolie.net.CommMessage;
import jolie.runtime.JavaService;
import jolie.runtime.Value;
import jolie.runtime.ValueVector;
import jolie.runtime.FaultException;
import jolie.runtime.embedding.RequestResponse;
public class HTTPServer extends JavaService
{
class SrvInfo
{
public HttpServer server;
public boolean started;
public String jolieOperation;
public String jolieResource;
public String host;
public int port;
}
Map <String, SrvInfo> servers = new HashMap<String, SrvInfo>();;
@RequestResponse
public void initialize(Value data) throws FaultException {
synchronized(servers) {
SrvInfo info = new SrvInfo();
info.host = data.getFirstChild("host").strValue();
info.port = data.getFirstChild("port").intValue();
info.jolieOperation = data.getFirstChild("operation").strValue();
info.jolieResource = data.getFirstChild("resource").strValue();
info.started = false;
info.server = null;
try {
info.server = HttpServer.create(new InetSocketAddress(
info.port), 0);
} catch (Exception e) {
throw new FaultException("cannot_listen", e);
}
servers.put(data.getFirstChild("id").strValue(), info);
}
}
@RequestResponse
public void addPage(Value page) throws FaultException {
synchronized(servers) {
// if no such server
if (!servers.containsKey(page.getFirstChild("id").strValue()))
throw new