From bb16a47ebb7fd9bf0e0f0e2499a7b893a8f9afc8 Mon Sep 17 00:00:00 2001 From: subjectxbj Date: Mon, 26 Mar 2018 10:46:14 +0000 Subject: [PATCH] Fix bugs and upload the web pages --- AlexaClientSDKConfig_default.json | 80 +++++++++++ config.c | 18 ++- lwa.c | 58 ++++---- lwa.h | 6 +- main.c | 162 ++++++++++++++++++---- web/AuthServer.py | 215 ++++++++++++++++++++++++++++++ web/authentication_failed.html | 63 +++++++++ web/img/logo.png | Bin 0 -> 4373 bytes web/img/logo_1.png | Bin 0 -> 4229 bytes web/img/logo_2.png | Bin 0 -> 4555 bytes web/img/logo_3.png | Bin 0 -> 22009 bytes web/logout.html | 96 +++++++++++++ web/splash_screen.html | 96 +++++++++++++ web/things_to_try.html | 128 ++++++++++++++++++ 14 files changed, 861 insertions(+), 61 deletions(-) create mode 100644 AlexaClientSDKConfig_default.json create mode 100644 web/AuthServer.py create mode 100755 web/authentication_failed.html create mode 100755 web/img/logo.png create mode 100755 web/img/logo_1.png create mode 100755 web/img/logo_2.png create mode 100755 web/img/logo_3.png create mode 100755 web/logout.html create mode 100755 web/splash_screen.html create mode 100755 web/things_to_try.html diff --git a/AlexaClientSDKConfig_default.json b/AlexaClientSDKConfig_default.json new file mode 100644 index 0000000..4484d1b --- /dev/null +++ b/AlexaClientSDKConfig_default.json @@ -0,0 +1,80 @@ + { + "authDelegate":{ + // The Client Secret of the Product from developer.amazon.com + "clientSecret":"70c955eaa6b998bb3eff97f051b18a5f72b21d38e3ba102da1e83aafdd0efa1f", + // Unique device serial number. e.g. 123456 + "deviceSerialNumber":"CXNK000ABCDE", + // Refresh Token populated by running AuthServer.py + "refreshToken":"", + // The Client ID of the Product from developer.amazon.com + "clientId":"amzn1.application-oa2-client.a308f3dc39a54349b970f19b13c86f5d", + // Product ID from developer.amazon.com + "productId":"GC4026E" + }, + "alertsCapabilityAgent":{ + // Path to Alerts database file. e.g. /home/ubuntu/Build/alerts.db + // Note: The directory specified must be valid. + // The database file (alerts.db) will be created by SampleApp, do not create it yourself. + // The database file should only be used for alerts (don't use it for other components of SDK) + "databaseFilePath":"", + // Path to default Alarm sound file. e.g. /home/ubuntu/alert_sounds/alarm_normal.mp3 + // Note: The audio file must exist and be a valid file. + "alarmSoundFilePath":"", + // Path to short Alarm sound file. e.g. /home/ubuntu/alert_sounds/alarm_short.wav + // Note: The audio file must exist and be a valid file. + "alarmShortSoundFilePath":"", + // Path to default timer sound file. e.g. /home/ubuntu/alert_sounds/timer_normal.mp3 + // Note: The audio file must exist and be a valid file. + "timerSoundFilePath":"", + // Path to short timer sound file. e.g. /home/ubuntu/alert_sounds/timer_short.wav + // Note: The audio file must exist and be a valid file. + "timerShortSoundFilePath":"" + }, + "settings":{ + // Path to Settings database file. e.g. /home/ubuntu/Build/settings.db + // Note: The directory specified must be valid. + // The database file (settings.db) will be created by SampleApp, do not create it yourself. + // The database file should only be used for settings (don't use it for other components of SDK) + "databaseFilePath":"", + "defaultAVSClientSettings":{ + // Default language for Alexa. + // See https://developer.amazon.com/docs/alexa-voice-service/settings.html#settingsupdated for valid values. + "locale":"" + } + }, + "certifiedSender":{ + // Path to Certified Sender database file. e.g. /home/ubuntu/Build/certifiedsender.db + // Note: The directory specified must be valid. + // The database file (certifiedsender.db) will be created by SampleApp, do not create it yourself. + // The database file should only be used for certifiedSender (don't use it for other components of SDK) + "databaseFilePath":"" + } + } + + +// Notes for logging +// The log levels are supported to debug when SampleApp is not working as expected. +// There are 14 levels of logging with DEBUG9 providing the highest level of logging and CRITICAL providing +// the lowest level of logging i.e. if DEBUG9 is specified while running the SampleApp, all the logs at DEBUG9 and +// below are displayed, whereas if CRITICAL is specified, only logs of CRITICAL are displayed. +// The 14 levels are: +// DEBUG9, DEBUG8, DEBUG7, DEBUG6, DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, DEBUG0, INFO, WARN, ERROR, CRTITICAL. + +// To selectively see the logging for a particular module, you can specify logging level in this json file. +// Some examples are: +// To only see logs of level INFO and below for ACL and MediaPlayer modules, +// - grep for ACSDK_LOG_MODULE in source folder. Find the log module for ACL and MediaPlayer. +// - Put the following in json: + +// "acl":{ +// "logLevel":"INFO" +// }, +// "mediaPlayer":{ +// "logLevel":"INFO" +// } + +// To enable DEBUG, build with cmake option -DCMAKE_BUILD_TYPE=DEBUG. By default it is built with RELEASE build. +// And run the SampleApp similar to the following command. +// e.g. TZ=UTC ./SampleApp /home/ubuntu/.../AlexaClientSDKConfig.json /home/ubuntu/KittAiModels/ DEBUG9" + + diff --git a/config.c b/config.c index fb3c193..01a4daa 100644 --- a/config.c +++ b/config.c @@ -67,7 +67,8 @@ json_object* getCleanConfig(char *filename) { //printf(cJSON_Print(json)); //printf("\n"); }else{ - printf("Failed to parse response\n"); + printf("Failed to parse config\n"); + return NULL; } return json; @@ -123,11 +124,20 @@ char *get_config_param_value(json_object *config, char *name){ } int update_config_param(json_object *config, char *name, char *value) { - json_object_object_del(config, name); + json_object *authDelegate = json_object_object_get(config, "authDelegate"); + if(authDelegate == NULL) { + return -1; + } + if (strcmp(name, "refreshToken") == 0){ + json_object_object_del(authDelegate, "refreshToken"); - json_object_object_add(config, name, json_object_new_string(value)); + json_object_object_add(authDelegate, "refreshToken", json_object_new_string(value)); + + return 0; + } - return 0; + return -1; } + diff --git a/lwa.c b/lwa.c index 19df30f..16abe6c 100644 --- a/lwa.c +++ b/lwa.c @@ -22,7 +22,7 @@ char * clientSecret = "70c955eaa6b998bb3eff97f051b18a5f72b21d38e3ba102da1e83aafd #endif char * scope = "alexa:all"; char * responseType = "code"; -char * urlencode(char *json_string) +static char * urlencode(char *json_string) { static char output[1024]; memset(output, 0, sizeof(output)); @@ -45,7 +45,7 @@ char * urlencode(char *json_string) } -char* getRedirectUrl(){ +static char* getRedirectUrl(json_object *config){ static char lwaUrl[1000]; char *scopeData; json_object *alexa_all, *attr; @@ -54,13 +54,13 @@ char* getRedirectUrl(){ #ifdef HARDCODE_CONFIG json_object_object_add(alexa_all, "productID", json_object_new_string(productId)); #else - json_object_object_add(alexa_all, "productID",json_object_new_string(get_config_param_value(json_config, "productId"))); + json_object_object_add(alexa_all, "productID",json_object_new_string(get_config_param_value(config, "productId"))); #endif json_object_object_add(alexa_all, "productInstanceAttributes", attr=json_object_new_object()); #ifdef HARDCODE_CONFIG json_object_object_add(attr, "deviceSerialNumber", json_object_new_string(deviceSerialNumber)); #else - json_object_object_add(attr, "deviceSerialNumber", json_object_new_string(get_config_param_value(json_config, "deviceSerialNumber"))); + json_object_object_add(attr, "deviceSerialNumber", json_object_new_string(get_config_param_value(config, "deviceSerialNumber"))); #endif scopeData = json_object_to_json_string(root); json_object_put(root); @@ -73,7 +73,7 @@ char* getRedirectUrl(){ #ifdef HARDCODE_CONFIG sprintf(param, "client_id=%s", clientId); #else - sprintf(param, "client_id=%s", get_config_param_value(json_config, "clientId")); + sprintf(param, "client_id=%s", get_config_param_value(config, "clientId")); #endif printf("param:%s\n",param); strcat(lwaUrl, param); @@ -103,7 +103,7 @@ char* getRedirectUrl(){ } -int parseResponse(char *response) { +static int parseResponse(char *response, json_object *config) { json_object *json = NULL; int ret; json = json_tokener_parse(response); @@ -113,15 +113,15 @@ int parseResponse(char *response) { char *refreshToken = json_object_get_string(refreshTokenObject); if(refreshToken) { printf("\n==============>Get Refresh Token: [%s]\n", refreshToken); - ret = update_config_param(json_config, "refreshToken", refreshToken); + ret = update_config_param(config, "refreshToken", refreshToken); if (ret != 0){ printf("\nERROR: Failed to update refresh token\n"); json_object_put(json); return -1; } - ret = writeConfig(json_config, config_out_path); + ret = writeConfig(config, config_path); if (ret != 0){ - printf("\nERROR: Failed to write config to [%s]\n", config_out_path); + printf("\nERROR: Failed to write config to [%s]\n", config_path); json_object_put(json); return -1; } @@ -139,14 +139,14 @@ int parseResponse(char *response) { } } char resp[10240]={0}; -size_t write_data(void* buffer, size_t size, size_t nmemb, void* response) { +static size_t write_data(void* buffer, size_t size, size_t nmemb, void* response) { int len = size*nmemb; memset(resp, 0, sizeof(resp)); memcpy(response, buffer, len); return len; } -int requestRefreshToken(char *code) { +static int requestRefreshToken(char *code, json_object *config) { CURL *curl; CURLcode res; int ret; @@ -170,12 +170,12 @@ int requestRefreshToken(char *code) { #ifdef HARDCODE_CONFIG clientId, #else - get_config_param_value(json_config, "clientId"), + get_config_param_value(config, "clientId"), #endif #ifdef HARDCODE_CONFIG clientSecret, #else - get_config_param_value(json_config, "clientSecret"), + get_config_param_value(config, "clientSecret"), #endif urlencode(redirect_uri) ); @@ -205,7 +205,7 @@ int requestRefreshToken(char *code) { printf("OK\n"); } printf("\n==============>Get RESPONSE: %s\n", resp); - ret = parseResponse(resp); + ret = parseResponse(resp, config); if (ret != 0){ printf("ERROR: parseResponse failed\n"); curl_easy_cleanup(curl); @@ -226,9 +226,20 @@ int requestRefreshToken(char *code) { } } -int handleUserRequest(int client) { +static int returnResult(int client, int status, char *info) { + char content[1024]={0}; + write(client, "HTTP/1.0 200\n",13); + write(client, "\n", 1); + if (status == 0){ + sprintf(content, "AVS LWA SuccessSuccess\n"); + }else{ + sprintf(content, "AVS LWA FailureFailed.%s\n", info); + } + write(client, content, strlen(content)); +} +int handleUserRequest(int client, json_object *config) { printf("\n==============>Receive User Request"); - char *redirectUrl = getRedirectUrl(); + char *redirectUrl = getRedirectUrl(config); if (redirectUrl == NULL) { return -1; } @@ -240,18 +251,7 @@ int handleUserRequest(int client) { write(client, "\n", 1); return 0; } -int returnResult(int client, int status, char *info) { - char content[1024]={0}; - write(client, "HTTP/1.0 200\n",13); - write(client, "\n", 1); - if (status == 0){ - sprintf(content, "AVS LWA SuccessSuccess\n"); - }else{ - sprintf(content, "AVS LWA FailureFailed.%s\n", info); - } - write(client, content, strlen(content)); -} -int handleAuthCodeGrant(int client, char *request) { +int handleAuthCodeGrant(int client, char *request, json_object *config) { char *tag, *code; int ret; printf("\n==============>Receive authresponse"); @@ -265,7 +265,7 @@ int handleAuthCodeGrant(int client, char *request) { returnResult(client, -1, "Failed to extract auth code"); return -1; } - ret = requestRefreshToken(code); + ret = requestRefreshToken(code, config); if (ret != 0){ printf("\nERROR: Failed to get refresh token\n"); returnResult(client, -1, "Failed to get refresh token"); diff --git a/lwa.h b/lwa.h index 0f4a469..fd7c07d 100644 --- a/lwa.h +++ b/lwa.h @@ -1,4 +1,4 @@ -extern char config_out_path[256]; +extern char config_path[256]; char redirect_uri[256]; -int handleUserRequest(int client); -int handleAuthCodeGrant(int client, char *request); +int handleUserRequest(int client, json_object *config); +int handleAuthCodeGrant(int client, char *request, json_object *config); diff --git a/main.c b/main.c index 773621e..f8ba872 100644 --- a/main.c +++ b/main.c @@ -17,16 +17,18 @@ #define CONNMAX 1000 #define BYTES 1024 #define PORT "3000" -#define CONFIG_IN "AlexaClientSDKConfig.json" -#define CONFIG_OUT "AlexaClientSDKConfig_out.json" +#define CONFIG "AlexaClientSDKConfig.json" +#define CONFIG_DEFAULT "AlexaClientSDKConfig_default.json" #define REDIRECT_URI "http://localhost:3000/authresponse" +#define WEBROOT "/etc/alexa/web/" -char config_in_path[256]={0}; -char config_out_path[256]={0}; +char config_path[256]={0}; +char config_default_path[256]={0}; char redirect_uri[256]={0}; char port[16]={0}; +char web_root[256]={0}; int listenfd, clients[CONNMAX]; -json_object *json_config = NULL; + void error(char *); @@ -46,20 +48,19 @@ int main(int argc, char** argv) int slot=0; - strcpy(config_in_path, CONFIG_IN); - strcpy(config_out_path, CONFIG_OUT); + strcpy(config_path, CONFIG); + strcpy(config_default_path, CONFIG_DEFAULT); strcpy(port, PORT); strcpy(redirect_uri, REDIRECT_URI); + strcpy(web_root, WEBROOT); + //Parsing the command line arguments - while ((c = getopt (argc, argv, "p:i:o:r:")) != -1) { + while ((c = getopt (argc, argv, "p:c:r:w:d:")) != -1) { switch (c) { - case 'i': - strcpy(config_in_path, optarg); - break; - case 'o': - strcpy(config_out_path, optarg); + case 'c': + strcpy(config_path, optarg); break; case 'p': strcpy(port,optarg); @@ -67,24 +68,25 @@ int main(int argc, char** argv) case 'r': strcpy(redirect_uri,optarg); break; + case 'w': + strcpy(web_root, optarg); + break; + case 'd': + strcpy(config_default_path, optarg); + break; default: printf("\nERROR: Unknown parameter\n"); exit(-1); } } - printf("AVS Auth Server started.\nPort: %s%s%s \nConfig in: %s%s%s \nConfig out: %s%s%s\nRedirect URI: %s%s%s\n", + printf("AVS Auth Server started.\nWebroot:%s%s%s \nPort: %s%s%s \nConfig: %s%s%s \nConfig default: %s%s%s \nRedirect URI: %s%s%s\n", + "\033[92m",web_root,"\033[0m", "\033[92m",port,"\033[0m", - "\033[92m",config_in_path,"\033[0m", - "\033[92m",config_out_path,"\033[0m", + "\033[92m",config_path,"\033[0m", + "\033[92m",config_default_path,"\033[0m", "\033[92m",redirect_uri,"\033[0m"); - json_config = getCleanConfig(config_in_path); - if (json_config == NULL) { - printf("ERROR: failed to parse the input config"); - exit(-1); - } - // Setting all elements to -1: signifies there is no client connected int i; for (i=0; i 0){ + printf("Get %d data\n", nread); + write(client, data_to_send, nread); + + } + close(fd); + +} + +int sendRedirect(int client, char *url) +{ + char content[1000]; + sprintf(content, "Location: %s\n",url); + write(client, "HTTP/1.0 307\n",13); + write(client, content, strlen(content)+1); + write(client, "\n", 1); + return 0; +} + +int copyFile(char *from, char *to) +{ + FILE *fpFrom, *fpTo; + char buffer[4096]; + int bytes; + fpFrom = fopen(from, "r"); + fpTo = fopen(to, "w+"); + + printf("\nCopy File from %s to %s\n", from , to); + + if(fpFrom == NULL){ + printf("\nFailed to open file %s", from); + return -1; + } + if(fpTo == NULL) { + printf("\nFailed to open file %s", to); + fclose(fpFrom); + return -1; + } + while((bytes = fread(buffer, 1, sizeof(buffer), fpFrom)) > 0){ + printf("#####get %d bytes\n", bytes); + fwrite(buffer, 1, bytes, fpTo); + } + fclose(fpFrom); + fclose(fpTo); + +} + +json_object* get_config(){ + json_object *config = getCleanConfig(config_path); + if (config == NULL) { + printf("\nINFO: Failed to open config \"%s\", use default config", config_path); + config = getCleanConfig(config_default_path); + if(config == NULL) + { + printf("\nERROR: Failed to open default config\"%s\". Exit.", config_default_path); + } + } + return config; +} + + //client connection void respond(int n) { @@ -194,17 +277,46 @@ void respond(int n) } else { + printf("\nRequest: %s", reqline[1]); + + json_object *json_config = NULL; + json_config = get_config(); + if(json_config == NULL) + { + printf("\nERROR: Failed to get config. Exit."); + exit(-1); + } if ( strncmp(reqline[1], "/\0", 2)==0 ) { - ret = handleUserRequest(clients[n]); + + char * refresh_token = get_config_param_value(json_config, "refreshToken"); + if (refresh_token == NULL || strlen(refresh_token) == 0){ + ret = sendFromDirectory(clients[n], web_root, "splash_screen.html"); + } + else + { + ret = sendFromDirectory(clients[n], web_root, "logout.html"); + } + }else if (strncmp(reqline[1], "/authresponse", 13)==0) { - ret = handleAuthCodeGrant(clients[n], reqline[1]); + ret = handleAuthCodeGrant(clients[n], reqline[1], json_config); if (ret == 0) { printf("\nGOOD: successfully generated the config with refresh token. Exit.\n"); - exit(0); + ret = sendFromDirectory(clients[n], web_root, "things_to_try.html"); } + }else if(strncmp(reqline[1], "/login", 6)==0) { + ret = handleUserRequest(clients[n], json_config); + }else if(strncmp(reqline[1], "/logout", 7)==0) { + copyFile(config_default_path, config_path); + ret = sendRedirect(clients[n], "/splash_screen.html"); } + else{ + ret = sendFromDirectory(clients[n], web_root, reqline[1]); + } + json_object_put(json_config); } + + } } diff --git a/web/AuthServer.py b/web/AuthServer.py new file mode 100644 index 0000000..3017ea5 --- /dev/null +++ b/web/AuthServer.py @@ -0,0 +1,215 @@ +# +# Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. +# + +from flask import Flask, redirect, request, send_from_directory +import requests +import json +import commentjson +import re + +from os.path import abspath, isfile, dirname +import sys +from urllib import urlencode +from shutil import copyfile + +# Shuts down the web-server. +def shutdown(): + func = request.environ.get('werkzeug.server.shutdown') + if func is None: + return 'You can close this window and terminate the script.' + else: + func() + return 'Server is shutting down, so you can close this window.' + +app = Flask(__name__) + +# Static redirect_uri. +redirectUri = 'http://localalexa.lan:3000/authresponse' + +# Static JSON key for the config file. +authDelegateKey = 'authDelegate' + +# Amazon LWA API URL. +amazonLwaApiUrl = 'https://api.amazon.com/auth/o2/token' + +# Amazon LWA API Request header. +amazonLwaApiHeaders = {'Content-Type': 'application/x-www-form-urlencoded'} + +# Default configuration filename, to be filled by CMake +defaultConfigFilename = "/etc/alexa/AlexaClientSDKConfig.json" + +# JSON keys for config file +CLIENT_ID = 'clientId' +CLIENT_SECRET = 'clientSecret' +PRODUCT_ID = 'productId' +DEVICE_SERIAL_NUMBER = 'deviceSerialNumber' +REFRESH_TOKEN = 'refreshToken' + +# Read the configuration filename from the command line arguments -if it exists. +if 2 == len(sys.argv): + configFilename = abspath(sys.argv[1]) +else: + # Assuming the script hasn't moved from the original location + # use the default location of SDKConfig.json using relative paths + configFilename = defaultConfigFilename + +# Check if the configuration file exists. +if not isfile(configFilename): + print 'The file "' + \ + configFilename + \ + '" does not exists. Please create this file and fill required data.' + sys.exit(1) + +try: + configFile = open(configFilename,'r') + +except IOError: + print 'File "' + configFilename + '" not found!' + sys.exit(1) +else: + with configFile: + configData = commentjson.load(configFile) + if not configData.has_key(authDelegateKey): + print 'The config file "' + \ + configFilename + \ + '" is missing the field "' + \ + authDelegateKey + \ + '".' + sys.exit(1) + else: + authDelegateDict = configData[authDelegateKey] + + +# Check if all required keys are parsed. +requiredKeys = [CLIENT_ID, CLIENT_SECRET, PRODUCT_ID, DEVICE_SERIAL_NUMBER] +try: + missingKey = requiredKeys[map(authDelegateDict.has_key,requiredKeys).index(False)]; + print 'Missing key: "' + missingKey + '". The list of required keys are:' + print ' * ' + '\n * '.join(requiredKeys) + print 'Exiting.' + sys.exit(1) +except ValueError: + pass +tokenIsValid=0; +# Refresh the refresh token to check if it really is a refresh token. +if authDelegateDict.has_key(REFRESH_TOKEN): + postData = { + 'grant_type': 'refresh_token', + 'refresh_token': authDelegateDict[REFRESH_TOKEN], + 'client_id': authDelegateDict[CLIENT_ID], + 'client_secret': authDelegateDict[CLIENT_SECRET]} + tokenRefreshRequest = requests.post( + amazonLwaApiUrl, + data=urlencode(postData), + headers=amazonLwaApiHeaders) + defaultRefreshTokenString = authDelegateDict[REFRESH_TOKEN]; + if 200 == tokenRefreshRequest.status_code: + print 'You have a valid refresh token already in the file.' +# sys.exit(0) + tokenIsValid=1; + else: + print 'The refresh request failed with the response code ' + \ + str(tokenRefreshRequest.status_code) + \ + ('. This might be due to a bad refresh token or bad client data. ' + 'We will continue with getting a refresh token, discarding the one in the file.\n') +else: + print 'Missing key: "' + REFRESH_TOKEN + sys.exit(0) +# The top page redirects to LWA page. + +root="/etc/alexa/web/" + +@app.route('/') +def index(): + if tokenIsValid: + return send_from_directory(root, "logout.html") + else: + return send_from_directory(root, "splash_screen.html") + + +@app.route("/") +def getfile(filename): + return send_from_directory(root, filename); + +@app.route('/logout') +def logout(): + copyfile("/etc/alexa/AlexaClientSDKConfigDefault.json", configFilename); + return redirect("/splash_screen.html"); + +@app.route('/login') +def login(): + scopeData = ('{{"alexa:all":' + '{{"productID":"{productId}",' + '"productInstanceAttributes":' + '{{"deviceSerialNumber":"{deviceSerialNumber}"}}}}}}').format( + productId=authDelegateDict[PRODUCT_ID], + deviceSerialNumber=authDelegateDict[DEVICE_SERIAL_NUMBER]) + lwaUrl = 'https://www.amazon.com/ap/oa/?' + urlencode({ + 'scope': 'alexa:all', + 'scope_data': scopeData, + 'client_id': authDelegateDict[CLIENT_ID], + 'response_type': 'code', + 'redirect_uri': redirectUri, + }) + return redirect(lwaUrl) + + +# The `authresponse` received from the redirect through LWA makes a POST to LWA API to get the token. +@app.route('/authresponse') +def get_refresh_token(): + postData = { + 'grant_type': 'authorization_code', + 'code': request.args.get('code',''), + 'client_id': authDelegateDict[CLIENT_ID], + 'client_secret': authDelegateDict[CLIENT_SECRET], + 'redirect_uri': redirectUri, + } + tokenRequest = requests.post( + amazonLwaApiUrl, + data=urlencode(postData), + headers=amazonLwaApiHeaders) + if not tokenRequest.json().has_key('refresh_token'): + return send_from_directory(root, "authentication_failed.html") + + authDelegateDict['refreshToken'] = tokenRequest.json()['refresh_token'] + try: + configFile = open(configFilename,'r') + except IOError: + print 'File "' + configFilename + '" cannot be opened!' + return '

The file "' + \ + configFilename + \ + '" cannot be opened, please check if the file is open elsewhere.
' + \ + shutdown() + \ + '

' + else: + fileContent = configFile.read() + try: + configFile = open(configFilename,'w') + except IOError: + print 'File "' + configFilename + '" cannot be opened!' + return '

The file "' + \ + configFilename + \ + '" cannot be opened, please check if the file is open elsewhere.
' + \ + shutdown() + \ + '

' + replaceWith = '\n\t\t\"refreshToken\":' + '\"' + tokenRequest.json()['refresh_token'] + '\",' + stringToFind = r"\s*\"\s*{}\s*\"\s*:\s*\"\s*".format(REFRESH_TOKEN) + \ + re.escape(defaultRefreshTokenString) + r"\s*\"\s*," + fileContent = re.sub(stringToFind, replaceWith, fileContent) + configFile.write(fileContent) + tokenIsValid=1 + + return send_from_directory(root, "things_to_try.html") +app.run(host='localalexa.lan',port=3000) diff --git a/web/authentication_failed.html b/web/authentication_failed.html new file mode 100755 index 0000000..25e9a97 --- /dev/null +++ b/web/authentication_failed.html @@ -0,0 +1,63 @@ + + + + + + + + + + Alexa + + + + +
+ +
+ +
amazon alexa
+
+
Something's gone wrong. Please try again.
+
+ + + diff --git a/web/img/logo.png b/web/img/logo.png new file mode 100755 index 0000000000000000000000000000000000000000..24a7448611f388e3b1dc2496afb071c7231ca180 GIT binary patch literal 4373 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 literal 0 HcmV?d00001 diff --git a/web/img/logo_1.png b/web/img/logo_1.png new file mode 100755 index 0000000000000000000000000000000000000000..3a764299e8410908d34381fc171d6ac92bc7cb05 GIT binary patch literal 4229 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 HobUYymG)dM literal 0 HcmV?d00001 diff --git a/web/img/logo_3.png b/web/img/logo_3.png new file mode 100755 index 0000000000000000000000000000000000000000..28123d1735d5cbbd4890dcce3abc44894525abc9 GIT binary patch literal 22009 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 RMec(XV?%R9>3PTS{{t+7NNNB8 literal 0 HcmV?d00001 diff --git a/web/logout.html b/web/logout.html new file mode 100755 index 0000000..d3d5cb8 --- /dev/null +++ b/web/logout.html @@ -0,0 +1,96 @@ + + + + + + + + + + Alexa + + + + +
+ +
+ +
amazon alexa
+
+
Your device includes access to Alexa.
+
Connect your Amazon account to
+
access personalized features.
+
+
+
Alexa allows you to use your voice to play music and get news,
+
sports scores, weather and more—all hands-free.
+
All you have to do is ask Alexa.
+
+ + + + diff --git a/web/splash_screen.html b/web/splash_screen.html new file mode 100755 index 0000000..c641316 --- /dev/null +++ b/web/splash_screen.html @@ -0,0 +1,96 @@ + + + + + + + + + + Alexa + + + + +
+ +
+ +
amazon alexa
+
+
Your device includes access to Alexa.
+
Connect your Amazon account to
+
access personalized features.
+
+
+
Alexa allows you to use your voice to play music and get news,
+
sports scores, weather and more—all hands-free.
+
All you have to do is ask Alexa.
+
+ + + + diff --git a/web/things_to_try.html b/web/things_to_try.html new file mode 100755 index 0000000..8811a16 --- /dev/null +++ b/web/things_to_try.html @@ -0,0 +1,128 @@ + + + + + + + + + + Alexa + + + + +
+ +
+ +
amazon alexa
+
+
You're ready to use Alexa. Here are some things to try:
+
+
+
+ + Alexa, play some jazz music. +
+
+
+ + Alexa, what's my Flash Briefing. +
+
+
+ + Alexa, what's your favorite movie. +
+
+
+
+
To learn more and access additional features, download the Amazon Alexa App.
+
+ + +