diff --git a/README.md b/README.md index fc55cb5..6d59f6f 100644 --- a/README.md +++ b/README.md @@ -23,13 +23,13 @@ Happy experimenting! Trying the samples is a good way to get started with using the SDK. -Getting the samples working has the following steps: setting up the DynamoDB table, importing the SDK and copying over the sample, creating the `keys.h` and `keys.cpp` files, and setting up the hardware. These steps are outlined for the samples for both the Spark core and Intel Galileo, so be sure you are following only the directions corresponding to the device and sample you are using. +Getting the samples working has the following steps: setting up the DynamoDB table, importing the SDK and copying over the sample, creating the `keys.h` and `keys.cpp` files, and setting up the hardware. These steps are outlined for the samples for both the Spark core and Intel Galileo, so be sure you are following only the directions corresponding to the device and sample you are using. If you are using a device other than Spark or Galileo, you may want to read through these steps anyway before implementing the interfaces in `DeviceIndependentInterfaces.cpp`/`.h` for your device. ### Step 1: Setting up the DynamoDB Table -For either device you will need to set up a DynamoDB table with the same name, hash key, and range key as in the sample you are using. These values are defined as constants in the sample, i.e. `HASH_KEY_NAME` and `TABLE_NAME`. +For either device you will need to set up a DynamoDB table with the same name, hash key, and range key as in the sample you are using. These values are defined as constants in the sample, i.e. `HASH_KEY_NAME` and `TABLE_NAME`. You can follow the steps below to get the tables set up with the right values, chosing the right set of instructions based on which sample you are using. @@ -65,7 +65,7 @@ This step is different for the Spark Core and Intel Galileo. #### Connected Maraca Sample (Edison/SparkCore/MediaTek) -follow the step by step guide: http://bit.ly/aws-iot-hackseries +follow the step by step guide: http://bit.ly/aws-iot-hackseries #### Intel Galileo/Edison Sample @@ -80,11 +80,11 @@ Create a new sketch with the Arduino IDE and copy and paste the sample code into #### Spark IO Core Sample -This assumes you already have your Spark set up and are able to program it with Spark Build. If you do not, head over to [Spark's website](http://docs.spark.io/). +This assumes you already have your Spark set up and are able to program it with Spark Build. If you do not, head over to [Spark's website](http://docs.spark.io/). Open up the Spark Build web page and create a new app. Name it whatever you would like. -Copy the contents of the sample you are using into the `.ino` file of your new app. +Copy the contents of the sample you are using into the `.ino` file of your new app. Next you need to import the SDK. Because the Spark Build IDE isn't local to your machine, you can't just `cp` the files over. Instead use the "+" tab in the top right corner of the Spark Build page to create a new file for each `.cpp`/`.h` file in the `src/` directory, except `GalileoAWSImplementations` and `AmazonKinesisClient`. Then copy and paste the contents of each file. @@ -99,7 +99,7 @@ You will need to create and add `keys.h` and `keys.cpp` into the `AWSArduinoSDK` #ifndef KEYS_H_ #define KEYS_H_ -extern const char* awsKeyID; // Declare these variables to +extern const char* awsKeyID; // Declare these variables to extern const char* awsSecKey; // be accessible by the sketch #endif @@ -129,7 +129,7 @@ Both spark samples use just one button connected to the D2 pin. #### Intel Galileo -This sample uses five buttons and a RGB LED. +This sample uses five buttons and a RGB LED. The RGB LED has the red leg connected to pin 6, the green leg connected to pin 9, and the blue leg connected to pin 10. @@ -145,4 +145,56 @@ For Galileo/Edison, after the wiring is finished, you should be able to connect For Spark, after the wiring is finished, you should be able to connect it to your computer via USB, and *Flash* the code. Be sure to refer to the comments in the samples for help. +#### ESP8266 +You can use these libraries with the [Arduino ESP8266](https://github.com/esp8266/arduino):. + +``` +#include +#include +#include "Esp8266AWSImplementations.h" + +Esp8266HttpClient httpClient; +Esp8266DateTimeProvider dateTimeProvider; + +AmazonIOTClient iotClient; +ActionError actionError; + +void setup() { + Serial.begin(115200); + delay(10); + + // Connect to WAP + Serial.print("Connecting to "); + Serial.println(ssid); + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + + iotClient.setAWSRegion("eu-west-1"); + iotClient.setAWSEndpoint("amazonaws.com"); + iotClient.setAWSDomain("foobar.iot.eu-west-1.amazonaws.com"); + iotClient.setAWSPath("/things/example-1/shadow"); + iotClient.setAWSKeyID("ID"); + iotClient.setAWSSecretKey("SECRET"); + iotClient.setHttpClient(&httpClient); + iotClient.setDateTimeProvider(&dateTimeProvider); +} + +void loop(){ + char* shadow = "{\"state\":{\"reported\": {\"foobar\": "bar"}}}"; + + char* result = iotClient.update_shadow(shadow, actionError); + Serial.print(result); + + delay(60000); +} + +``` diff --git a/src/common/AWSClient2.cpp b/src/common/AWSClient2.cpp index bb178a0..7353e58 100644 --- a/src/common/AWSClient2.cpp +++ b/src/common/AWSClient2.cpp @@ -280,7 +280,7 @@ char* AWSClient2::createRequest(MinimalString &reqPayload) { if (awsRegion == 0 || awsEndpoint == 0 || awsSecKey == 0 || awsKeyID == 0 || httpClient == 0 || dateTimeProvider == 0) return 0; - + createRequestInit(reqPayload); char* request = headersToRequest(); createRequestCleanup(); diff --git a/src/common/AWSClient2.h b/src/common/AWSClient2.h index c3d0828..37e2634 100644 --- a/src/common/AWSClient2.h +++ b/src/common/AWSClient2.h @@ -12,13 +12,13 @@ #include "AWSFoundationalTypes.h" /* Total number of headers. */ -static const int HEADER_COUNT = 7; +static const int HEADER_COUNT2 = 7; /* Size of the awsDate string. */ -static const int AWS_DATE_LEN = 8; +static const int AWS_DATE_LEN2 = 8; /* Size of the awsTime string. */ -static const int AWS_TIME_LEN = 6; +static const int AWS_TIME_LEN2 = 6; /* Size of sha hashes and signatures in hexidecimal. */ -static const int HASH_HEX_LEN = 64; +static const int HASH_HEX_LEN2 = 64; /* Base class for an AWS Service Client. Creates http and https request in raw * http format or as a curl command. */ @@ -32,15 +32,15 @@ class AWSClient2 { /* The user's AWS Access Key ID for accessing the AWS Resource. */ char* awsKeyID; /* GMT date in yyyyMMdd format. */ - char awsDate[AWS_DATE_LEN + 1]; + char awsDate[AWS_DATE_LEN2 + 1]; /* GMT time in HHmmss format. */ - char awsTime[AWS_TIME_LEN + 1]; + char awsTime[AWS_TIME_LEN2 + 1]; /* Number of headers created. */ int headersCreated; /* Array of the created http headers. */ - char* headers[HEADER_COUNT]; + char* headers[HEADER_COUNT2]; /* Array of string lengths of the headers in the "headers" array. */ - int headerLens[HEADER_COUNT]; + int headerLens[HEADER_COUNT2]; /* The payload of the httprequest to be created */ MinimalString payload; diff --git a/src/common/AWSClient4.cpp b/src/common/AWSClient4.cpp new file mode 100644 index 0000000..97e03a8 --- /dev/null +++ b/src/common/AWSClient4.cpp @@ -0,0 +1,230 @@ +/* + * AWSClient.cpp + * + * See AWSClient.h for description. + * See http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html + * + */ + +#include "AWSClient4.h" +#include "Utils.h" +#include "DeviceIndependentInterfaces.h" +#include "AWSFoundationalTypes.h" +#include "sha256.h" +#include +#include +#include + +AWSClient4::AWSClient4() { + /* Null until set in init method. */ + awsRegion = 0; + awsEndpoint = 0; + awsSecKey = 0; + awsKeyID = 0; + httpClient = 0; + dateTimeProvider = 0; + method = 0; + uri = 0; +} + +void AWSClient4::setAWSRegion(const char * awsRegion) { + int len = strlen(awsRegion) + 1; + this->awsRegion = new char[len](); + strcpy(this->awsRegion, awsRegion); +} +void AWSClient4::setAWSEndpoint(const char * awsEndpoint) { + int len = strlen(awsEndpoint) + 1; + this->awsEndpoint = new char[len](); + strcpy(this->awsEndpoint, awsEndpoint); +} +void AWSClient4::setAWSDomain(const char * awsDomain) { + int len = strlen(awsDomain) + 1; + this->awsDomain = new char[len](); + strcpy(this->awsDomain, awsDomain); +} +void AWSClient4::setAWSPath(const char * awsPath) { + int len = strlen(awsPath) + 1; + this->awsPath = new char[len](); + strcpy(this->awsPath, awsPath); +} +void AWSClient4::setAWSSecretKey(const char * awsSecKey) { + int len = strlen(awsSecKey) + 1; + this->awsSecKey = new char[len](); + strcpy(this->awsSecKey, awsSecKey); +} +void AWSClient4::setAWSKeyID(const char * awsKeyID) { + int len = strlen(awsKeyID) + 1; + this->awsKeyID = new char[len](); + strcpy(this->awsKeyID, awsKeyID); +} +void AWSClient4::setHttpClient(IHttpClient* httpClient) { + this->httpClient = httpClient; +} +void AWSClient4::setDateTimeProvider(IDateTimeProvider* dateTimeProvider) { + this->dateTimeProvider = dateTimeProvider; +} + +AWSClient4::~AWSClient4() { + if (awsRegion != 0) + delete[] awsRegion; + if (awsEndpoint != 0) + delete[] awsEndpoint; + if (awsSecKey != 0) + delete[] awsSecKey; + if (awsKeyID != 0) + delete[] awsKeyID; +} + + +char* AWSClient4::createCanonicalHeaders() { + // headers, alphabetically sorted, lowercase, eg: key:value + // content-type:x + // host:host + // x-amz-content-sha256:hash + // x-amz-date:date + char canonical_headers[500] = ""; + sprintf(canonical_headers, "%scontent-type:%s\n", canonical_headers, contentType); + sprintf(canonical_headers, "%shost:%s\n", canonical_headers, awsDomain); + // sprintf(canonical_headers, "%srange:bytes=0-9\n", canonical_headers); // s3 + sprintf(canonical_headers, "%sx-amz-content-sha256:%s\n", canonical_headers, payloadHash); + sprintf(canonical_headers, "%sx-amz-date:%sT%sZ\n\n", canonical_headers, awsDate, awsTime); + return canonical_headers; +} + +char* AWSClient4::createRequestHeaders(char* signature) { + char headers[1000] = ""; + sprintf(headers, "%sContent-Type: %s\r\n", headers, contentType); + sprintf(headers, "%sConnection: close\r\n", headers); + sprintf(headers, "%sContent-Length: %d\r\n", headers, strlen(payload.getCStr())); + sprintf(headers, "%sHost: %s\r\n", headers, awsDomain); + sprintf(headers, "%sx-amz-content-sha256: %s\r\n", headers, payloadHash); + sprintf(headers, "%sx-amz-date: %sT%sZ\r\n", headers, awsDate, awsTime); + sprintf(headers, "%sAuthorization: AWS4-HMAC-SHA256 Credential=%s/%s/%s/%s/aws4_request,SignedHeaders=%s,Signature=%s\r\n", headers, awsKeyID, awsDate, awsRegion, awsService, signedHeaders, signature); + return headers; +} + +char* AWSClient4::createStringToSign(char* canonical_request) { + // return canonical_request; + SHA256* sha256 = new SHA256(); + char* hashed = (*sha256)(canonical_request, strlen(canonical_request)); + delete sha256; + // return canonical_request; + + char string_to_sign[700] = ""; + sprintf(string_to_sign, "%sAWS4-HMAC-SHA256\n", string_to_sign); + sprintf(string_to_sign, "%s%sT%sZ\n", string_to_sign, awsDate, awsTime); + sprintf(string_to_sign, "%s%s/%s/%s/aws4_request\n", string_to_sign, awsDate, awsRegion, awsService); + + sprintf(string_to_sign, "%s%s", string_to_sign, hashed); + + return string_to_sign; +} + +char* AWSClient4::createCanonicalRequest() { + char canonical_request[800] = ""; + sprintf(canonical_request, "%s%s\n", canonical_request, method); // VERB + sprintf(canonical_request, "%s%s\n", canonical_request, awsPath); // URI + sprintf(canonical_request, "%s%s\n", canonical_request, queryString); // queryString + + char* headers = createCanonicalHeaders(); + + sprintf(canonical_request, "%s%s", canonical_request, headers); // headers + sprintf(canonical_request, "%s%s\n", canonical_request, signedHeaders); // signed_headers + sprintf(canonical_request, "%s%s", canonical_request, payloadHash); // payload + + return canonical_request; +} + + +char* AWSClient4::createSignature(const char* toSign) { + + /* Allocate memory for the signature */ + char* signature = new char[HASH_HEX_LEN4 + 1](); + + /* Create the signature key */ + /* + 4 for "AWS4" */ + int keyLen = strlen(awsSecKey) + 4; + char* key = new char[keyLen + 1](); + sprintf(key, "AWS4%s", awsSecKey); + + /* repeatedly apply hmac with the appropriate values. See + * http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html + * for algorithm. */ + char* k1 = hmacSha256(key, keyLen, awsDate, strlen(awsDate)); + delete[] key; + char* k2 = hmacSha256(k1, SHA256_DEC_HASH_LEN, awsRegion, + strlen(awsRegion)); + delete[] k1; + char* k3 = hmacSha256(k2, SHA256_DEC_HASH_LEN, awsService, + strlen(awsService)); + delete[] k2; + char* k4 = hmacSha256(k3, SHA256_DEC_HASH_LEN, "aws4_request", 12); + delete[] k3; + char* k5 = hmacSha256(k4, SHA256_DEC_HASH_LEN, toSign, strlen(toSign)); + delete[] k4; + + /* Convert the chars in hash to hex for signature. */ + for (int i = 0; i < SHA256_DEC_HASH_LEN; ++i) { + sprintf(signature + 2 * i, "%02lx", 0xff & (unsigned long) k5[i]); + } + delete[] k5; + return signature; +} + + +char* AWSClient4::createRequest(MinimalString &reqPayload) { + /* Check that all values have been initialized. */ + if (awsRegion == 0 || awsEndpoint == 0 || awsSecKey == 0 || awsKeyID == 0 + || httpClient == 0 || dateTimeProvider == 0) + return 0; + + // set date and time + // @TODO: find out why sprintf doesn't work + const char* dateTime = dateTimeProvider->getDateTime(); + strncpy(awsDate, dateTime, 8); + awsDate[9] = '\0'; + strncpy(awsTime, dateTime + 8, 6); + awsTime[7] = '\0'; + + SHA256* sha256 = new SHA256(); + payloadHash = (*sha256)(reqPayload.getCStr(), reqPayload.length()); + delete sha256; + + payload = reqPayload; + + // create the canonical request, we need to copy the results + // @TODO: figure out why the reference doesn't work + char *canonical_request_return = createCanonicalRequest(); + char canonical_request[1000]; + strcpy(canonical_request, canonical_request_return); + // return canonical_request; + + // create the signing string, we need to copy the results + // @TODO: figure out why the reference doesn't work + char *return_string_to_sign = createStringToSign(canonical_request); + char string_to_sign[500]; + strcpy(string_to_sign, return_string_to_sign); + + // create the signature + char *signature = createSignature(string_to_sign); + + // create the headers + char *headers = createRequestHeaders(signature); + + // get the host/domain + // char *host = createHost(); + + // create the request with all the vars + char* request = new char[strlen(method) + strlen(awsDomain) + strlen(awsPath) + strlen(headers) + strlen(reqPayload.getCStr()) + 16](); + sprintf(request, "%s %s HTTP/1.1\r\n%s\r\n%s\r\n\r\n", method, awsPath, headers, reqPayload.getCStr()); + + return request; +} + +char* AWSClient4::sendData(const char* data) { + // char* server = createHost(); + int port = httpS ? 443 : 80; + char* response = httpClient->send(data, awsDomain, port); + // delete[] server; + return response; +} diff --git a/src/common/AWSClient4.h b/src/common/AWSClient4.h new file mode 100644 index 0000000..184e055 --- /dev/null +++ b/src/common/AWSClient4.h @@ -0,0 +1,116 @@ +/* + * AWSClient2.h + * + * Base classes for services to create an HTTP request. + * + */ + +#ifndef AWSCLIENT4_H_ +#define AWSCLIENT4_H_ + +#include "DeviceIndependentInterfaces.h" +#include "AWSFoundationalTypes.h" + +/* Total number of headers. */ +static const int HEADER_COUNT4 = 7; +/* Size of the awsDate string. */ +static const int AWS_DATE_LEN4 = 8; +/* Size of the awsTime string. */ +static const int AWS_TIME_LEN4 = 6; +/* Size of sha hashes and signatures in hexidecimal. */ +static const int HASH_HEX_LEN4 = 64; + +/* Base class for an AWS Service Client. Creates http and https request in raw + * http format or as a curl command. */ +class AWSClient4 { + /* Name of region, eg. "us-east-1" in "kinesis.us-east-1.amazonaws.com". */ + char* awsRegion; + /* Endpoint, eg. "amazonaws.com" in "kinesis.us-east-1.amazonaws.com". */ + char* awsEndpoint; + /* Domain, optional, eg. "A2MBBEONHC9LUG.iot.us-east-1.amazonaws.com". */ + char* awsDomain; + /* Path, optional eg. "/things/foobar/shadow", eg for iot-data. */ + char* awsPath; + /* The user's AWS Secret Key for accessing the AWS Resource. */ + char* awsSecKey; + /* The user's AWS Access Key ID for accessing the AWS Resource. */ + char* awsKeyID; + /* GMT date in yyyyMMdd format. */ + char awsDate[AWS_DATE_LEN4 + 1]; + /* GMT time in HHmmss format. */ + char awsTime[AWS_TIME_LEN4 + 1]; + + /* The payload of the httprequest to be created */ + MinimalString payload; + + // /* Add the headers that will be signed to the headers array. Called before + // * createStringToSign. */ + // void initSignedHeaders(); + // /* Create the canonical request and the string to sign as described. Return + // * value must be deleted by caller. */ + char* createStringToSign(char* canonical_request); + // /* Given the string to sign, create the signature (a 64-char cstring). + // * Return value must be deleted by caller. */ + char* createSignature(const char* toSign); + char* createRequestHeaders(char* signature); + // /* Add the headers that will not be signed to the headers array. Called + // * after createSignature. */ + // void initUnsignedHeaders(const char* signature); + // /* Contains all of the work to be done before headersToRequest or + // * headersToCurlRequest are called. Takes the payload to be sent and the + // * GMT date in yyyyMMddHHmmss format. */ + // void createRequestInit(MinimalString &reqPayload); + // /* Clean up after headersToRequest or headersToCurlRequest are called. */ + // void createRequestCleanup(); + // /* Using the headers array, create a raw http request. */ + // char* headersToRequest(void); + +protected: + char* method; + char* uri; + char* queryString; + char* headers; + char* signedHeaders; + char* payloadHash; + + /* Used to keep track of time. */ + IDateTimeProvider* dateTimeProvider; + /* Used to send http to the server. */ + IHttpClient* httpClient; + /* true if https is to be used, false if http is to be used. */ + bool httpS; + /* Name of service, eg. "kinesis" in "kinesis.us-east-1.amazonaws.com". */ + const char* awsService; + /* Content type of payload, eg. "application/x-amz-json-1.1". */ + const char* contentType; + // /* Generates the host based on subdomain, service, etc */ + char* createHost(void); + /* Creates a raw http request, given the payload and current GMT date in + * yyyyMMddHHmmss format. Should be exposed to user by extending class. + * Returns 0 if client is unititialized. */ + char* createRequest(MinimalString &payload); + char* createCanonicalRequest(); + char* createCanonicalHeaders(); + + /* Sends http data. Returns http response, or null on error. */ + char* sendData(const char* data); + /* Empty constructor. Must also be initialized with init. */ + AWSClient4(); + +public: + /* Setters for values used by createRequest and createCurlRequest. Must + * be set or create[Curl]Request will return null. */ + /* Generates the host based on subdomain, service, etc */ + char* createHostString(void); + void setAWSRegion(const char * awsRegion); + void setAWSEndpoint(const char * awsEndpoint); + void setAWSDomain(const char * awsDomain); + void setAWSPath(const char * awsPath); + void setAWSSecretKey(const char * awsSecKey); + void setAWSKeyID(const char * awsKeyID); + void setHttpClient(IHttpClient* httpClient); + void setDateTimeProvider(IDateTimeProvider* dateTimeProvider); + ~AWSClient4(void); +}; + +#endif /* AWSCLIENT4_H_ */ diff --git a/src/common/AmazonDynamoDBClient.cpp b/src/common/AmazonDynamoDBClient.cpp index f2c1430..b49579e 100755 --- a/src/common/AmazonDynamoDBClient.cpp +++ b/src/common/AmazonDynamoDBClient.cpp @@ -704,9 +704,9 @@ MinimalString AttributeValue::jsonSerialize() const { return map.jsonSerialize(); } -void AttributeValue::setSS(MinimalList SSS) { +void AttributeValue::setSS(MinimalList SS) { SSBeenSet = true; - this->SSS = SSS; + this->SS = SS; } void AttributeValue::setBS(MinimalList BS) { @@ -6747,4 +6747,3 @@ UpdateTableOutput AmazonDynamoDBClient::updateTable(UpdateTableInput updateTable } return updateTableOutput; } - diff --git a/src/common/AmazonDynamoDBClient.h b/src/common/AmazonDynamoDBClient.h index d5bcefa..f4fed23 100755 --- a/src/common/AmazonDynamoDBClient.h +++ b/src/common/AmazonDynamoDBClient.h @@ -153,7 +153,7 @@ class ProvisionedThroughputDescription{ /*

Represents the data for an attribute. You can set one, and only one, of the elements.

*/ class AttributeValue{ - MinimalList SSS; + MinimalList SS; MinimalList BS; MinimalString B; MinimalString S; diff --git a/src/common/AmazonIOTClient.cpp b/src/common/AmazonIOTClient.cpp new file mode 100644 index 0000000..0d85eca --- /dev/null +++ b/src/common/AmazonIOTClient.cpp @@ -0,0 +1,23 @@ +#include "AmazonIOTClient.h" +#include "AWSFoundationalTypes.h" +#include +#include "Utils.h" + +AmazonIOTClient::AmazonIOTClient() : AWSClient4() { + this->awsService = "iotdata"; + this->contentType = "application/json"; + this->signedHeaders = "content-type;host;x-amz-content-sha256;x-amz-date"; + this->uri = "/"; + this->queryString = ""; + this->httpS = true; +} + +char* AmazonIOTClient::update_shadow(MinimalString shadow, ActionError& actionError) { + actionError = NONE_ACTIONERROR; + + this->method = "POST"; + char* request = createRequest(shadow); + // return request; + char* response = sendData(request); + return response; +} diff --git a/src/common/AmazonIOTClient.h b/src/common/AmazonIOTClient.h new file mode 100644 index 0000000..292bcac --- /dev/null +++ b/src/common/AmazonIOTClient.h @@ -0,0 +1,22 @@ +#ifndef AMAZONIOTCLIENT_H_ +#define AMAZONIOTCLIENT_H_ +#include "AWSClient4.h" + + +// class Shadow { +// MinimalString shadow; +// void reset(); +// public: +// void setShadow(shadow) const; +// }; + + + +class AmazonIOTClient : public AWSClient4 { +public: + AmazonIOTClient(); + + char* update_shadow(MinimalString shadow, ActionError& actionError); +}; + +#endif /* AMAZONIOTCLIENT_H_ */ diff --git a/src/common/AmazonKinesisClient.cpp b/src/common/AmazonKinesisClient.cpp index 7cd876c..5c6973f 100644 --- a/src/common/AmazonKinesisClient.cpp +++ b/src/common/AmazonKinesisClient.cpp @@ -2354,4 +2354,3 @@ KinesisErrorCheckingOnlyOutput AmazonKinesisClient::splitShard(SplitShardInput s } return kinesisErrorCheckingOnlyOutput; } - diff --git a/src/common/AmazonS3Client.cpp b/src/common/AmazonS3Client.cpp new file mode 100644 index 0000000..d230f59 --- /dev/null +++ b/src/common/AmazonS3Client.cpp @@ -0,0 +1,24 @@ +#include "AmazonS3Client.h" +#include "AWSFoundationalTypes.h" +#include +#include "Utils.h" + +// stub for verifying if the signature actually works, needs to get some more love :) + +AmazonS3Client::AmazonS3Client() : AWSClient4() { + this->awsService = "s3"; + this->signedHeaders = "host;range;x-amz-content-sha256;x-amz-date"; + this->queryString = ""; + this->httpS = true; +} + +char* AmazonS3Client::get(MinimalString uri, ActionError& actionError) { + actionError = NONE_ACTIONERROR; + + this->method = "GET"; + setAWSPath(uri.getCStr()); + MinimalString foo = ""; + char* request = createRequest(foo); + // char* response = sendData(request); + return request; +} diff --git a/src/common/AmazonS3Client.h b/src/common/AmazonS3Client.h new file mode 100644 index 0000000..216aeff --- /dev/null +++ b/src/common/AmazonS3Client.h @@ -0,0 +1,11 @@ +#ifndef AMAZONS3CLIENT_H_ +#define AMAZONS3CLIENT_H_ +#include "AWSClient4.h" + +class AmazonS3Client : public AWSClient4 { +public: + AmazonS3Client(); + char* get(MinimalString uri, ActionError& actionError); +}; + +#endif /* AMAZONS3CLIENT_H_ */ diff --git a/src/common/ESP8266AWSImplementations.h b/src/common/ESP8266AWSImplementations.h new file mode 120000 index 0000000..c10521d --- /dev/null +++ b/src/common/ESP8266AWSImplementations.h @@ -0,0 +1 @@ +/Users/svdgraaf/Documents/Arduino/Libraries/aws-sdk-arduino/src/esp8266/ESP8266AWSImplementations.h \ No newline at end of file diff --git a/src/common/ESP8266AWSImplentations.cpp b/src/common/ESP8266AWSImplentations.cpp new file mode 120000 index 0000000..1e73c50 --- /dev/null +++ b/src/common/ESP8266AWSImplentations.cpp @@ -0,0 +1 @@ +/Users/svdgraaf/Documents/Arduino/Libraries/aws-sdk-arduino/src/esp8266/ESP8266AWSImplentations.cpp \ No newline at end of file diff --git a/src/esp8266/ESP8266AWSImplementations.h b/src/esp8266/ESP8266AWSImplementations.h new file mode 100644 index 0000000..75260c2 --- /dev/null +++ b/src/esp8266/ESP8266AWSImplementations.h @@ -0,0 +1,37 @@ +#ifndef AWSESP2866IMPLEMENTATIONS_H_ +#define AWSESP2866IMPLEMENTATIONS_H_ +#include "DeviceIndependentInterfaces.h" +/* application.h is Esp8266's standard library. Define TCPClient. */ +#include + +/* HttpClient implementation to be used on the Esp8266 Core device. */ +class Esp8266HttpClient: public IHttpClient { + WiFiClientSecure sclient; + //TCPClient client; +public: + Esp8266HttpClient(); + /* Send http request and return the response. */ + char* send(const char *request, const char* serverUrl, int port); + /* Returns false. Client uses raw http/https. */ + bool usesCurl(void); +}; + +class Esp8266DateTimeProvider: public IDateTimeProvider { + /* The time as a cstring in yyyyMMddHHmmss format. Is written to within and + * returned by getDateTime(). */ + WiFiClient client; + //char dateTime[15]; +public: + char dateTime[15]; + Esp8266DateTimeProvider(); + /* Retrieve the current GMT date and time in yyyyMMddHHmmss format. */ + const char* getDateTime(void); + /* Returns false because Esp8266 has it's own mechanism for syncing that does + * not require an argument. */ + bool syncTakesArg(void); + /* Synchronizes Esp8266's date and time with Esp8266's servers. The dateTime + * argument is ignored. */ + void sync(const char* dateTime); +}; + +#endif /* AWSESP2866IMPLEMENTATIONS_H_ */ diff --git a/src/esp8266/ESP8266AWSImplentations.cpp b/src/esp8266/ESP8266AWSImplentations.cpp new file mode 100644 index 0000000..607517b --- /dev/null +++ b/src/esp8266/ESP8266AWSImplentations.cpp @@ -0,0 +1,158 @@ +#include +/* application.h is Esp8266's standard library. Defines the Arduino String + * object, the Arduino delay() procedure, and the Esp8266 TCPClient. */ +#include "Esp8266AWSImplementations.h" +#include "DeviceIndependentInterfaces.h" +#include +#include + +int delayTime = 500; +char* updateCurTime(void); + +Esp8266HttpClient::Esp8266HttpClient() { +} + +char* Esp8266HttpClient::send(const char* request, const char* serverUrl, int port) { + + WiFiClientSecure sclient; + Serial.println(serverUrl); + Serial.println(port); + Serial.println(request); + Serial.println(""); + Serial.println(""); + + // + String response = ""; + if (sclient.connect(serverUrl, port)) { + + // Send the request + sclient.print(request); + + // keep reading the response until it's finished + while(sclient.connected()) { + while(sclient.available()){ + char c = sclient.read(); + response.concat(c); + Serial.print('.'); + } + + // disconnect any open connections + sclient.stop(); + } + + } else { + // connection was unsuccessful + sclient.stop(); + return "can't setup SSL connection"; + } + + // convert the string into a char and return + int len = response.length(); + char* response_char = new char[len + 1](); + response.toCharArray(response_char, len + 1); + return response_char; +} + +bool Esp8266HttpClient::usesCurl() { + /* Does not use curl command. */ + return false; +} + +Esp8266DateTimeProvider::Esp8266DateTimeProvider() { +} + +const char* Esp8266DateTimeProvider::getDateTime() { + return updateCurTime(); +} +bool Esp8266DateTimeProvider::syncTakesArg(void) { + return true; +} + +void Esp8266DateTimeProvider::sync(const char* dateTime) { + // should have no need for an implementation +} + +//////////////////////////////////// +// convert month to digits +//////////////////////////////////// +String getMonth(String sM) { + if(sM=="Jan") return "01"; + if(sM=="Feb") return "02"; + if(sM=="Mar") return "03"; + if(sM=="Apr") return "04"; + if(sM=="May") return "05"; + if(sM=="Jun") return "06"; + if(sM=="Jul") return "07"; + if(sM=="Aug") return "08"; + if(sM=="Sep") return "09"; + if(sM=="Oct") return "10"; + if(sM=="Nov") return "11"; + if(sM=="Dec") return "12"; + return "01"; +} + +//////////////////////////////////// +// Scrape UTC Time from server +//////////////////////////////////// +char* updateCurTime(void) { + static int timeout_busy=0; + int ipos; + timeout_busy=0; //reset + + const char* timeServer = "aws.amazon.com"; + + // send a bad header on purpose, so we get a 400 with a DATE: timestamp + const char* timeServerGet = "GET example.com/ HTTP/1.1"; + String utctime; + String GmtDate; + static char dateStamp[20]; + static char chBuf[200]; + char utctimeraw[80]; + char* dpos; + + WiFiClient client; + if (client.connect(timeServer, 80)) { + //Send Request + client.println(timeServerGet); + client.println(); + while((!client.available())&&(timeout_busy++<5000)){ + // Wait until the client sends some data + delay(1); + } + + // kill client if timeout + if(timeout_busy>=5000) { + client.flush(); + client.stop(); + Serial.println("timeout receiving timeserver data\n"); + return dateStamp; + } + + // read the http GET Response + String req2 = client.readString(); + // Serial.println(""); + // Serial.println(""); + // Serial.print(req2); + // Serial.println(""); + // Serial.println(""); + + // close connection + delay(1); + client.flush(); + client.stop(); + + ipos = req2.indexOf("Date:"); + if(ipos>0) { + GmtDate = req2.substring(ipos,ipos+35); + // Serial.println(GmtDate); + utctime = GmtDate.substring(18,22) + getMonth(GmtDate.substring(14,17)) + GmtDate.substring(11,13) + GmtDate.substring(23,25) + GmtDate.substring(26,28) + GmtDate.substring(29,31); + // Serial.println(utctime.substring(0,14)); + utctime.substring(0,14).toCharArray(dateStamp, 20); + } + } + else { + Serial.println("did not connect to timeserver\n"); + } + timeout_busy=0; // reset timeout + return dateStamp; // Return latest or last good dateStamp +}