├── .ck-crowdnode ├── ck-crowdnode-config.json.linux.sample └── ck-crowdnode-config.json.windows.sample ├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── COPYRIGHT.txt ├── LICENSE.txt ├── NOTES.txt ├── README.md ├── _compile.bat ├── _compile.sh ├── _compile_and_run_with_valgrind.sh ├── _compile_raw.bat ├── _compile_raw2.bat ├── appveyor.yml ├── run_tests.py ├── src ├── base64.c ├── base64.h ├── cJSON.c ├── cJSON.h ├── ck-crowdnode-server.c ├── net_uuid.c ├── net_uuid.h ├── urldecoder.c └── urldecoder.h └── tests ├── ck-master.zip ├── non-latin.txt ├── non-latin.txt.win ├── test_push_pull.py ├── test_shell.py └── test_state.py /.ck-crowdnode/ck-crowdnode-config.json.linux.sample: -------------------------------------------------------------------------------- 1 | { 2 | "port":3333, 3 | "path_to_files":"$HOME/ck-crowdnode-files", 4 | "secret_key":"c4e239b4-8471-11e6-b24d-cbfef11692ca" 5 | } 6 | -------------------------------------------------------------------------------- /.ck-crowdnode/ck-crowdnode-config.json.windows.sample: -------------------------------------------------------------------------------- 1 | { 2 | "port":3333, 3 | "path_to_files":"%LOCALAPPDATA%\\ck-crowdnode-files", 4 | "secret_key":"c4e239b4-8471-11e6-b24d-cbfef11692ca" 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.pyc 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | script: "./_compile.sh && python -m run_tests.py" 3 | compiler: 4 | - clang 5 | - gcc -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(ck-crowdnode) 4 | 5 | set(SRC 6 | src/net_uuid.h 7 | src/net_uuid.c 8 | 9 | 10 | src/base64.h 11 | src/base64.c 12 | src/cJSON.h 13 | src/cJSON.c 14 | src/urldecoder.c 15 | src/ck-crowdnode-server.c 16 | ) 17 | 18 | add_executable(ck-crowdnode-server ${SRC}) 19 | 20 | IF(WIN32) 21 | 22 | target_link_libraries(ck-crowdnode-server ws2_32) 23 | 24 | install( TARGETS ck-crowdnode-server RUNTIME DESTINATION bin COMPONENT Applications) 25 | 26 | include(InstallRequiredSystemLibraries) 27 | 28 | set(CPACK_GENERATOR NSIS) 29 | set(CPACK_PACKAGE_NAME "ck-crowdnode-server") 30 | set(CPACK_PACKAGE_VENDOR "cTuning.org") 31 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "CK crowd-node server") 32 | set(CPACK_PACKAGE_VERSION "0.0.1") 33 | set(CPACK_PACKAGE_VERSION_MAJOR "0") 34 | set(CPACK_PACKAGE_VERSION_MINOR "0") 35 | set(CPACK_PACKAGE_VERSION_PATCH "1") 36 | set(CPACK_PACKAGE_INSTALL_DIRECTORY "CK crowd-node server") 37 | set(CPACK_NSIS_MODIFY_PATH ON) 38 | set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}\\\\LICENSE.txt") 39 | set(CPACK_PACKAGE_EXECUTABLES ck-crowdnode-server "CK crowd-node server") 40 | 41 | include(CPack) 42 | 43 | ELSE(WIN32) 44 | target_link_libraries(ck-crowdnode-server m) 45 | ENDIF(WIN32) 46 | -------------------------------------------------------------------------------- /COPYRIGHT.txt: -------------------------------------------------------------------------------- 1 | (C)opyright 2016 cTuning foundation, dividiti and contributors 2 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2016 cTuning foundation, dividiti and volunteers 2 | All rights reserved 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of CTUNING FOUNDATION nor the name of DIVIDITI 15 | nor the names of its contributors may be used to endorse 16 | or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 23 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /NOTES.txt: -------------------------------------------------------------------------------- 1 | Assorted notes: 2 | Android and C: 3 | http://www.ibm.com/developerworks/opensource/tutorials/os-androidndk/index.html 4 | https://www.quora.com/Is-there-any-app-on-Android-which-allows-us-to-run-our-C-C++-Java-programs-on-Android-for-practice 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Linux/MacOS: [![Build Status](https://travis-ci.org/ctuning/ck-crowdnode.svg?branch=master)](https://travis-ci.org/ctuning/ck-crowdnode) 2 | Windows: [![Windows Build status](https://ci.appveyor.com/api/projects/status/587t15ai8bocr7t4?svg=true)](https://ci.appveyor.com/project/gfursin/ck-crowdnode) 3 | 4 | Collective Knowledge Node for experiment crowdsourcing (on Windows devices) 5 | =========================================================================== 6 | 7 | Standalone, thin and portable server to let users participate in experiment 8 | crowdsourcing via Collective Knowledge. It unifies remote execution on Windows 9 | similar to Android ADB (experiment crowdsourcing via Linux and Android 10 | platforms is already supported by CK). It can also be used to create farms 11 | of machines for collaborative benchmarking and tuning (crowd-benchmarking). 12 | 13 | Note that both server and client should run Windows. 14 | 15 | Project homepage: 16 | * http://cknowledge.org 17 | * http://cTuning.org 18 | 19 | License 20 | ======= 21 | * Permissive 3-clause BSD license. (See `LICENSE.txt` for more details). 22 | 23 | Status 24 | ====== 25 | 26 | Relatively stable - testing phase 27 | 28 | Usage: server side 29 | ================== 30 | On Windows: 31 | 32 | 1. Download the installer from [Appveyor](https://ci.appveyor.com/project/gfursin/ck-crowdnode/build/artifacts) 33 | 34 | 2. Install and start "CK crowd-node server". 35 | 36 | 3. Write down "[INFO for CK client]" - you will require this info to configure this target machine on a client. 37 | 38 | Alternatively, you can build it manually (you'll need CMake and Visual Studio): 39 | 40 | ``` 41 | mkdir build 42 | cd build 43 | cmake .. 44 | cmake --build . --config Release --target PACKAGE 45 | ``` 46 | 47 | Now, you should be able to run `build/Release/ck-crowdnode-server.exe`. 48 | 49 | Usage: client side 50 | ================== 51 | Install [CK framework](http://github.com/ctuning/ck). 52 | If you have PIP, you can install it simply as following: 53 | 54 | ``` 55 | $ pip install ck 56 | ``` 57 | 58 | Pull ck-autotuning repository (including dependencies): 59 | 60 | ``` 61 | $ ck pull repo:ck-autotuning 62 | ``` 63 | 64 | Prepare local file with a secret key (see [INFO for CK client]), 65 | for example in C:\secret-key.txt 66 | 67 | Register target machine with ck-crowdnode-server via 68 | (substitute ''my-remote-target'' with any other user-friendly name) 69 | 70 | ``` 71 | $ ck add machine:my-remote-target 72 | ``` 73 | 74 | Select 4) CK: remote Windows machine accessed via CK crowd node. 75 | Then 4) windows-64 76 | 77 | Then enter hostname, port, path to public key (C:\secret-key.txt), 78 | and full path to files on a target machine (all info is available 79 | via [INFO for CK client] - we later plan to automate this process). 80 | 81 | Now you can check that you machine is connected and online via 82 | 83 | ``` 84 | $ ck show machine 85 | ``` 86 | 87 | or 88 | 89 | ``` 90 | $ ck browse machine 91 | ``` 92 | 93 | Now you should be able to compile and run sample program using this target. 94 | You need to have Microsoft C compilers and Microsoft SDK installed 95 | (there is a free edition available). You can also download and install 96 | [LLVM for Windows](http://llvm.org/releases/download.html), 97 | but remember that it also requires Visual C compiler and Microsoft SDK. 98 | 99 | Try to compile susan benchmark (during first compilation, CK will attempt 100 | to automatically detect installed compilers and SDK while asking 101 | you extra questions, if needed): 102 | 103 | ``` 104 | $ ck compile program:cbench-automotive-susan --speed --target=my-remote-target 105 | ``` 106 | 107 | Finally, you can try to run it: 108 | 109 | ``` 110 | $ ck run program:cbench-automotive-susan --target=my-remote-target 111 | ``` 112 | 113 | If everything is configured correctly, this code will be executed several 114 | times on a required target and execution time will be reported! 115 | 116 | If you have any problems, questions or comments, do not hesitate to get in touch 117 | with the CK community via [our public mailing list](https://groups.google.com/forum/#!forum/collective-knowledge open CK mailing list)! 118 | 119 | Further details 120 | =============== 121 | * https://github.com/ctuning/ck 122 | * https://github.com/ctuning/ck/wiki/Publications 123 | * https://github.com/ctuning/ck/wiki/Farms-of-CK-machines 124 | 125 | Public discussions 126 | ================== 127 | * [CK mailing list](http://groups.google.com/group/collective-knowledge) 128 | 129 | ![logo](https://github.com/ctuning/ck-guide-images/blob/master/logo-validated-by-the-community-simple.png) 130 | -------------------------------------------------------------------------------- /_compile.bat: -------------------------------------------------------------------------------- 1 | call ck set env tags=compiler,lang-c --local --bat_file=tmp-ck-env.bat --bat_new --print && call tmp-ck-env.bat && del /Q tmp-ck-env.bat 2 | if %errorlevel% neq 0 exit /b %errorlevel% 3 | 4 | call ck set env tags=tool,cmake --local --bat_file=tmp-ck-env.bat --bat_new --print && call tmp-ck-env.bat && del /Q tmp-ck-env.bat 5 | if %errorlevel% neq 0 exit /b %errorlevel% 6 | 7 | mkdir build 8 | cd build 9 | 10 | cmake .. 11 | 12 | msbuild /p:Configuration=Release /p:Platform=Win32 Project.sln 13 | -------------------------------------------------------------------------------- /_compile.sh: -------------------------------------------------------------------------------- 1 | mkdir build 2 | cd build 3 | 4 | cmake .. 5 | 6 | make 7 | -------------------------------------------------------------------------------- /_compile_and_run_with_valgrind.sh: -------------------------------------------------------------------------------- 1 | mkdir build 2 | cd build 3 | 4 | rm * 5 | 6 | cmake -DCMAKE_BUILD_TYPE=Debug .. 7 | 8 | make 9 | 10 | valgrind --tool=memcheck ./ck-crowdnode-server 11 | -------------------------------------------------------------------------------- /_compile_raw.bat: -------------------------------------------------------------------------------- 1 | call ck set env tags=compiler,lang-c --local --bat_file=tmp-ck-env.bat --bat_new --print && call tmp-ck-env.bat && del /Q tmp-ck-env.bat 2 | if %errorlevel% neq 0 exit /b %errorlevel% 3 | 4 | call ck set env tags=tool,cmake --local --bat_file=tmp-ck-env.bat --bat_new --print && call tmp-ck-env.bat && del /Q tmp-ck-env.bat 5 | if %errorlevel% neq 0 exit /b %errorlevel% 6 | 7 | mkdir build 8 | cd build 9 | 10 | cl ../src/*.c /Feck-crowdnode-server.exe /link wsock32.lib 11 | -------------------------------------------------------------------------------- /_compile_raw2.bat: -------------------------------------------------------------------------------- 1 | mkdir build 2 | cd build 3 | 4 | cl ../src/*.c /Feck-crowdtune.exe /link wsock32.lib 5 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - PYTHON: "C:\\Python27" 4 | 5 | build: 6 | 7 | build_script: 8 | - mkdir build 9 | - cd build 10 | - cmake .. 11 | - cmake --build . --config Release --target PACKAGE 12 | - cd .. 13 | - "%PYTHON%/python.exe -m run_tests.py --server_executable build/Release/ck-crowdnode-server.exe" 14 | 15 | artifacts: 16 | - path: build/*.exe 17 | name: CK Crowdnode Server installer 18 | -------------------------------------------------------------------------------- /run_tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from __future__ import print_function 4 | import subprocess 5 | import shutil 6 | import os 7 | import sys 8 | import unittest 9 | import time 10 | import platform 11 | import argparse 12 | 13 | def safe_remove(fname): 14 | try: 15 | os.remove(fname) 16 | except Exception: 17 | pass 18 | 19 | arg_parser = argparse.ArgumentParser(description='Run crowdnode tests') 20 | arg_parser.add_argument('--server_executable', default='build/ck-crowdnode-server', 21 | help='path to the crowdnode executable') 22 | arg_parser.add_argument('--ck_dir', 23 | help='path to the CK kernel directory. If not given, the newest kernel will be downloaded') 24 | args = arg_parser.parse_args() 25 | 26 | script_dir = os.path.dirname(os.path.realpath(__file__)) 27 | os.chdir(script_dir) 28 | 29 | config_dir = os.path.join(script_dir, '.ck-crowdnode') 30 | config_file = os.path.join(config_dir, 'ck-crowdnode-config.json') 31 | config_file_sample_windows = os.path.join(config_dir, 'ck-crowdnode-config.json.windows.sample') 32 | config_file_sample_linux = os.path.join(config_dir, 'ck-crowdnode-config.json.linux.sample') 33 | 34 | files_dir = os.path.join(script_dir, 'ck-crowdnode-files') 35 | if not os.path.exists(files_dir): 36 | os.makedirs(files_dir) 37 | 38 | node_process = None 39 | remove_ck_dir = args.ck_dir is None 40 | 41 | def die(retcode): 42 | os.chdir(script_dir) 43 | if remove_ck_dir: 44 | shutil.rmtree(ck_dir, ignore_errors=True) 45 | shutil.rmtree(files_dir, ignore_errors=True) 46 | safe_remove(config_file) 47 | if node_process is not None: 48 | node_process.kill() 49 | exit(retcode) 50 | 51 | safe_remove(config_file) 52 | node_env = os.environ.copy() 53 | if 'Windows' == platform.system(): 54 | node_env['LOCALAPPDATA'] = script_dir 55 | shutil.copyfile(config_file_sample_windows, config_file) 56 | else: 57 | node_env['HOME'] = script_dir 58 | shutil.copyfile(config_file_sample_linux, config_file) 59 | 60 | node_process = subprocess.Popen([args.server_executable], env=node_env) 61 | 62 | ck_dir=args.ck_dir 63 | if args.ck_dir is None: 64 | ck_dir='test-ck-master' 65 | shutil.rmtree(ck_dir, ignore_errors=True) 66 | r = subprocess.call('git clone https://github.com/ctuning/ck.git ' + ck_dir, shell=True) 67 | if 0 < r: 68 | print('Error: failed to clone CK!') 69 | die(1) 70 | 71 | sys.path.append(ck_dir) 72 | 73 | import ck.kernel as ck 74 | 75 | test_repo_name = 'ck-crowdnode-auto-tests' 76 | test_repo_cid = test_repo_name + '::' 77 | r = ck.access({'module_uoa': 'repo', 'data_uoa': test_repo_name, 'action': 'remove', 'force': 'yes', 'all': 'yes'}) 78 | r = ck.access({'remote': 'yes', 'module_uoa': 'repo', 'url': 'http://localhost:3333', 'quiet': 'yes', 'data_uoa': test_repo_name, 'action': 'add'}) 79 | if r['return']>0: 80 | print('Unable to create test repo. ' + r.get('error', '')) 81 | die(1) 82 | 83 | tests_dir = os.path.join(script_dir, 'tests') 84 | 85 | module_cfg = { 86 | 'secret_key': 'c4e239b4-8471-11e6-b24d-cbfef11692ca', 87 | 'platform': platform.system(), 88 | 'repo_name': test_repo_name, 89 | 'cid': test_repo_cid 90 | } 91 | 92 | def access_test_repo(param_dict, checkFail=True): 93 | d = {'secretkey': module_cfg['secret_key'], 'cid': module_cfg['cid']} 94 | d.update(param_dict) 95 | r = ck.access(d) 96 | if checkFail and r['return']>0: 97 | raise AssertionError('Failed to access test repo. Call parameters:\n ' + str(d) + '\n') 98 | return r 99 | 100 | class CkTestLoader(unittest.TestLoader): 101 | def loadTestsFromModule(self, module, pattern=None): 102 | module.ck = ck 103 | module.cfg = module_cfg 104 | module.access_test_repo = access_test_repo 105 | module.files_dir = files_dir 106 | return unittest.TestLoader.loadTestsFromModule(self, module, pattern) 107 | 108 | suite = CkTestLoader().discover(tests_dir, pattern='test_*.py') 109 | 110 | os.chdir(tests_dir) 111 | test_result = unittest.TextTestRunner().run(suite) 112 | 113 | die(0 if test_result.wasSuccessful() else 1) 114 | -------------------------------------------------------------------------------- /src/base64.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "base64.h" 6 | 7 | /** 8 | * characters used for Base64 encoding 9 | */ 10 | const char *BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 11 | 12 | /** 13 | * encode three bytes using base64 (RFC 3548) 14 | * 15 | * @param triple three bytes that should be encoded 16 | * @param result buffer of four characters where the result is stored 17 | */ 18 | void _base64_encode_triple(unsigned char triple[3], char result[4]) { 19 | int tripleValue, i; 20 | 21 | tripleValue = triple[0]; 22 | tripleValue *= 256; 23 | tripleValue += triple[1]; 24 | tripleValue *= 256; 25 | tripleValue += triple[2]; 26 | 27 | for (i=0; i<4; i++) { 28 | result[3-i] = BASE64_CHARS[tripleValue%64]; 29 | tripleValue /= 64; 30 | } 31 | } 32 | 33 | /** 34 | * encode an array of bytes using Base64 (RFC 3548) 35 | * 36 | * @param source the source buffer 37 | * @param sourcelen the length of the source buffer 38 | * @param target the target buffer 39 | * @param targetlen the length of the target buffer 40 | * @return 1 on success, 0 otherwise 41 | */ 42 | int base64_encode(unsigned char *source, size_t sourcelen, char *target, size_t targetlen) { 43 | /* check if the result will fit in the target buffer */ 44 | 45 | if ((sourcelen+2)/3*4 > targetlen-1) 46 | return 0; 47 | 48 | /* encode all full triples */ 49 | while (sourcelen >= 3) { 50 | _base64_encode_triple(source, target); 51 | sourcelen -= 3; 52 | source += 3; 53 | target += 4; 54 | } 55 | 56 | /* encode the last one or two characters */ 57 | if (sourcelen > 0) { 58 | unsigned char temp[3]; 59 | memset(temp, 0, sizeof(temp)); 60 | memcpy(temp, source, sourcelen); 61 | _base64_encode_triple(temp, target); 62 | target[3] = '='; 63 | if (sourcelen == 1) 64 | target[2] = '='; 65 | 66 | target += 4; 67 | } 68 | 69 | /* terminate the string */ 70 | target[0] = 0; 71 | 72 | return 1; 73 | } 74 | 75 | /** 76 | * determine the value of a base64 encoding character 77 | * 78 | * @param base64char the character of which the value is searched 79 | * @return the value in case of success (0-63), -1 on failure 80 | */ 81 | int _base64_char_value(char base64char) { 82 | if (base64char >= 'A' && base64char <= 'Z') 83 | return base64char-'A'; 84 | if (base64char >= 'a' && base64char <= 'z') 85 | return base64char-'a'+26; 86 | if (base64char >= '0' && base64char <= '9') 87 | return base64char-'0'+2*26; 88 | if (base64char == '-') //was + -> + 89 | return 2*26+10;//return 43;//(int)('+'); 90 | if (base64char == '_') // was / -> / 91 | return 2*26+11;//return 47;//(int)('/'); 92 | return -1; 93 | } 94 | 95 | /** 96 | * decode a 4 char base64 encoded byte triple 97 | * 98 | * @param quadruple the 4 characters that should be decoded 99 | * @param result the decoded data 100 | * @return lenth of the result (1, 2 or 3), 0 on failure 101 | */ 102 | int _base64_decode_triple(char quadruple[4], char result[3]) { 103 | int i, triple_value, bytes_to_decode = 3, only_equals_yet = 1; 104 | int char_value[4]; 105 | 106 | for (i=0; i<4; i++) 107 | char_value[i] = _base64_char_value(quadruple[i]); 108 | 109 | /* check if the characters are valid */ 110 | for (i=3; i>=0; i--) { 111 | if (char_value[i]<0) { 112 | if (only_equals_yet && quadruple[i]=='=') { 113 | /* we will ignore this character anyway, make it something 114 | * that does not break our calculations */ 115 | char_value[i]=0; 116 | bytes_to_decode--; 117 | continue; 118 | } 119 | return 0; 120 | } 121 | /* after we got a real character, no other '=' are allowed anymore */ 122 | only_equals_yet = 0; 123 | } 124 | 125 | /* if we got "====" as input, bytes_to_decode is -1 */ 126 | if (bytes_to_decode < 0) 127 | bytes_to_decode = 0; 128 | 129 | /* make one big value out of the partial values */ 130 | triple_value = char_value[0]; 131 | triple_value *= 64; 132 | triple_value += char_value[1]; 133 | triple_value *= 64; 134 | triple_value += char_value[2]; 135 | triple_value *= 64; 136 | triple_value += char_value[3]; 137 | 138 | /* break the big value into bytes */ 139 | for (i=bytes_to_decode; i<3; i++) 140 | triple_value /= 256; 141 | for (i=bytes_to_decode-1; i>=0; i--) { 142 | result[i] = triple_value%256; 143 | triple_value /= 256; 144 | } 145 | 146 | return bytes_to_decode; 147 | } 148 | 149 | /** 150 | * decode base64 encoded data 151 | * 152 | * @param source the encoded data (zero terminated) 153 | * @param target pointer to the target buffer 154 | * @param targetlen length of the target buffer 155 | * @return length of converted data on success, -1 otherwise 156 | */ 157 | size_t base64_decode(char *source, unsigned char *target, size_t targetlen) { 158 | char *src, *tmpptr; 159 | char quadruple[4]; 160 | unsigned char tmpresult[3]; 161 | int i, tmplen = 3; 162 | size_t converted = 0; 163 | 164 | /* concatinate '===' to the source to handle unpadded base64 data */ 165 | src = (char *)malloc(strlen(source)+5); 166 | if (src == NULL) 167 | return -1; 168 | strcpy(src, source); 169 | strcat(src, "===="); 170 | tmpptr = src; 171 | 172 | unsigned char *startTarget = target; 173 | 174 | /* convert as long as we get a full result */ 175 | while (tmplen == 3) { 176 | /* get 4 characters to convert */ 177 | for (i=0; i<4; i++) { 178 | /* skip invalid characters - we won't reach the end */ 179 | while (*tmpptr != '=' && _base64_char_value(*tmpptr)<0) 180 | tmpptr++; 181 | quadruple[i] = *(tmpptr++); 182 | } 183 | 184 | /* convert the characters */ 185 | tmplen = _base64_decode_triple(quadruple, tmpresult); 186 | 187 | /* check if the fit in the result buffer */ 188 | if (targetlen < tmplen) { 189 | free(src); 190 | return -1; 191 | } 192 | 193 | /* put the partial result in the result buffer */ 194 | memcpy(target, tmpresult, tmplen); 195 | target += tmplen; 196 | targetlen -= tmplen; 197 | converted += tmplen; 198 | } 199 | 200 | free(src); 201 | return converted; 202 | } -------------------------------------------------------------------------------- /src/base64.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * encode three bytes using base64 (RFC 3548) 5 | * 6 | * @param triple three bytes that should be encoded 7 | * @param result buffer of four characters where the result is stored 8 | */ 9 | void _base64_encode_triple(unsigned char triple[3], char result[4]); 10 | 11 | /** 12 | * encode an array of bytes using Base64 (RFC 3548) 13 | * 14 | * @param source the source buffer 15 | * @param sourcelen the length of the source buffer 16 | * @param target the target buffer 17 | * @param targetlen the length of the target buffer 18 | * @return 1 on success, 0 otherwise 19 | */ 20 | int base64_encode(unsigned char *source, size_t sourcelen, char *target, size_t targetlen); 21 | 22 | /** 23 | * determine the value of a base64 encoding character 24 | * 25 | * @param base64char the character of which the value is searched 26 | * @return the value in case of success (0-63), -1 on failure 27 | */ 28 | int _base64_char_value(char base64char) ; 29 | 30 | /** 31 | * decode a 4 char base64 encoded byte triple 32 | * 33 | * @param quadruple the 4 characters that should be decoded 34 | * @param result the decoded data 35 | * @return lenth of the result (1, 2 or 3), 0 on failure 36 | */ 37 | int _base64_decode_triple(char quadruple[4], char result[3]) ; 38 | 39 | /** 40 | * decode base64 encoded data 41 | * 42 | * @param source the encoded data (zero terminated) 43 | * @param target pointer to the target buffer 44 | * @param targetlen length of the target buffer 45 | * @return length of converted data on success, -1 otherwise 46 | */ 47 | size_t base64_decode(char *source, unsigned char *target, size_t targetlen); 48 | -------------------------------------------------------------------------------- /src/cJSON.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009 Dave Gamble 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* 24 | Slightly updated by Grigori Fursin to change printing format to 25 | similar python format from function json.dump(array, indent=2) 26 | */ 27 | 28 | /* cJSON */ 29 | /* JSON parser in C. */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include "cJSON.h" 39 | 40 | static const char *ep; 41 | 42 | const char *cJSON_GetErrorPtr() {return ep;} 43 | 44 | static int cJSON_strcasecmp(const char *s1,const char *s2) 45 | { 46 | if (!s1) return (s1==s2)?0:1;if (!s2) return 1; 47 | for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; 48 | return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); 49 | } 50 | 51 | static void *(*cJSON_malloc)(size_t sz) = malloc; 52 | static void (*cJSON_free)(void *ptr) = free; 53 | 54 | static char* cJSON_strdup(const char* str) 55 | { 56 | size_t len; 57 | char* copy; 58 | 59 | len = strlen(str) + 1; 60 | if (!(copy = (char*)cJSON_malloc(len))) return 0; 61 | memcpy(copy,str,len); 62 | return copy; 63 | } 64 | 65 | void cJSON_InitHooks(cJSON_Hooks* hooks) 66 | { 67 | if (!hooks) { /* Reset hooks */ 68 | cJSON_malloc = malloc; 69 | cJSON_free = free; 70 | return; 71 | } 72 | 73 | cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; 74 | cJSON_free = (hooks->free_fn)?hooks->free_fn:free; 75 | } 76 | 77 | /* Internal constructor. */ 78 | static cJSON *cJSON_New_Item() 79 | { 80 | cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); 81 | if (node) memset(node,0,sizeof(cJSON)); 82 | return node; 83 | } 84 | 85 | /* Delete a cJSON structure. */ 86 | void cJSON_Delete(cJSON *c) 87 | { 88 | cJSON *next; 89 | while (c) 90 | { 91 | next=c->next; 92 | if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); 93 | if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); 94 | if (c->string) cJSON_free(c->string); 95 | cJSON_free(c); 96 | c=next; 97 | } 98 | } 99 | 100 | /* Parse the input text to generate a number, and populate the result into item. */ 101 | static const char *parse_number(cJSON *item,const char *num) 102 | { 103 | double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; 104 | 105 | /* Could use sscanf for this? */ 106 | if (*num=='-') sign=-1,num++; /* Has sign? */ 107 | if (*num=='0') num++; /* is zero */ 108 | if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ 109 | if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ 110 | if (*num=='e' || *num=='E') /* Exponent? */ 111 | { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ 112 | while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ 113 | } 114 | 115 | n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ 116 | 117 | item->valuedouble=n; 118 | item->valueint=(int)n; 119 | item->type=cJSON_Number; 120 | return num; 121 | } 122 | 123 | /* Render the number nicely from the given item into a string. */ 124 | static char *print_number(cJSON *item) 125 | { 126 | char *str; 127 | double d=item->valuedouble; 128 | if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) 129 | { 130 | str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ 131 | if (str) sprintf(str,"%d",item->valueint); 132 | } 133 | else 134 | { 135 | str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ 136 | if (str) 137 | { 138 | if (fabs(floor(d)-d)<=DBL_EPSILON) sprintf(str,"%.0f",d); 139 | else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); 140 | else sprintf(str,"%f",d); 141 | } 142 | } 143 | return str; 144 | } 145 | 146 | /* Parse the input text into an unescaped cstring, and populate item. */ 147 | static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; 148 | static const char *parse_string(cJSON *item,const char *str) 149 | { 150 | const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; 151 | if (*str!='\"') {ep=str;return 0;} /* not a string! */ 152 | 153 | while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ 154 | 155 | out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ 156 | if (!out) return 0; 157 | 158 | ptr=str+1;ptr2=out; 159 | while (*ptr!='\"' && *ptr) 160 | { 161 | if (*ptr!='\\') *ptr2++=*ptr++; 162 | else 163 | { 164 | ptr++; 165 | switch (*ptr) 166 | { 167 | case 'b': *ptr2++='\b'; break; 168 | case 'f': *ptr2++='\f'; break; 169 | case 'n': *ptr2++='\n'; break; 170 | case 'r': *ptr2++='\r'; break; 171 | case 't': *ptr2++='\t'; break; 172 | case 'u': /* transcode utf16 to utf8. */ 173 | sscanf(ptr+1,"%4x",&uc);ptr+=4; /* get the unicode char. */ 174 | 175 | if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; // check for invalid. 176 | 177 | if (uc>=0xD800 && uc<=0xDBFF) // UTF16 surrogate pairs. 178 | { 179 | if (ptr[1]!='\\' || ptr[2]!='u') break; // missing second-half of surrogate. 180 | sscanf(ptr+3,"%4x",&uc2);ptr+=6; 181 | if (uc2<0xDC00 || uc2>0xDFFF) break; // invalid second-half of surrogate. 182 | uc=0x10000 | ((uc&0x3FF)<<10) | (uc2&0x3FF); 183 | } 184 | 185 | len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; 186 | 187 | switch (len) { 188 | case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; 189 | case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; 190 | case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; 191 | case 1: *--ptr2 =(uc | firstByteMark[len]); 192 | } 193 | ptr2+=len; 194 | break; 195 | default: *ptr2++=*ptr; break; 196 | } 197 | ptr++; 198 | } 199 | } 200 | *ptr2=0; 201 | if (*ptr=='\"') ptr++; 202 | item->valuestring=out; 203 | item->type=cJSON_String; 204 | return ptr; 205 | } 206 | 207 | /* Render the cstring provided to an escaped version that can be printed. */ 208 | static char *print_string_ptr(const char *str) 209 | { 210 | const char *ptr;char *ptr2,*out;int len=0;unsigned char token; 211 | 212 | if (!str) return cJSON_strdup(""); 213 | ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} 214 | 215 | out=(char*)cJSON_malloc(len+3); 216 | if (!out) return 0; 217 | 218 | ptr2=out;ptr=str; 219 | *ptr2++='\"'; 220 | while (*ptr) 221 | { 222 | if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; 223 | else 224 | { 225 | *ptr2++='\\'; 226 | switch (token=*ptr++) 227 | { 228 | case '\\': *ptr2++='\\'; break; 229 | case '\"': *ptr2++='\"'; break; 230 | case '\b': *ptr2++='b'; break; 231 | case '\f': *ptr2++='f'; break; 232 | case '\n': *ptr2++='n'; break; 233 | case '\r': *ptr2++='r'; break; 234 | case '\t': *ptr2++='t'; break; 235 | default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ 236 | } 237 | } 238 | } 239 | *ptr2++='\"';*ptr2++=0; 240 | return out; 241 | } 242 | /* Invote print_string_ptr (which is useful) on an item. */ 243 | static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} 244 | 245 | /* Predeclare these prototypes. */ 246 | static const char *parse_value(cJSON *item,const char *value); 247 | static char *print_value(cJSON *item,int depth,int fmt); 248 | static const char *parse_array(cJSON *item,const char *value); 249 | static char *print_array(cJSON *item,int depth,int fmt); 250 | static const char *parse_object(cJSON *item,const char *value); 251 | static char *print_object(cJSON *item,int depth,int fmt); 252 | 253 | /* Utility to jump whitespace and cr/lf */ 254 | static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} 255 | 256 | /* Parse an object - create a new root, and populate. */ 257 | cJSON *cJSON_Parse(const char *value) 258 | { 259 | cJSON *c=cJSON_New_Item(); 260 | ep=0; 261 | if (!c) return 0; /* memory fail */ 262 | 263 | if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;} 264 | return c; 265 | } 266 | 267 | /* Render a cJSON item/entity/structure to text. */ 268 | char *cJSON_Print(cJSON *item) {return print_value(item,0,1);} 269 | char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} 270 | 271 | /* Parser core - when encountering text, process appropriately. */ 272 | static const char *parse_value(cJSON *item,const char *value) 273 | { 274 | if (!value) return 0; /* Fail on null. */ 275 | if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } 276 | if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } 277 | if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } 278 | if (*value=='\"') { return parse_string(item,value); } 279 | if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } 280 | if (*value=='[') { return parse_array(item,value); } 281 | if (*value=='{') { return parse_object(item,value); } 282 | 283 | ep=value;return 0; /* failure. */ 284 | } 285 | 286 | /* Render a value to text. */ 287 | static char *print_value(cJSON *item,int depth,int fmt) 288 | { 289 | char *out=0; 290 | if (!item) return 0; 291 | switch ((item->type)&255) 292 | { 293 | case cJSON_NULL: out=cJSON_strdup("null"); break; 294 | case cJSON_False: out=cJSON_strdup("false");break; 295 | case cJSON_True: out=cJSON_strdup("true"); break; 296 | case cJSON_Number: out=print_number(item);break; 297 | case cJSON_String: out=print_string(item);break; 298 | case cJSON_Array: out=print_array(item,depth,fmt);break; 299 | case cJSON_Object: out=print_object(item,depth,fmt);break; 300 | } 301 | return out; 302 | } 303 | 304 | /* Build an array from input text. */ 305 | static const char *parse_array(cJSON *item,const char *value) 306 | { 307 | cJSON *child; 308 | if (*value!='[') {ep=value;return 0;} /* not an array! */ 309 | 310 | item->type=cJSON_Array; 311 | value=skip(value+1); 312 | if (*value==']') return value+1; /* empty array. */ 313 | 314 | item->child=child=cJSON_New_Item(); 315 | if (!item->child) return 0; /* memory fail */ 316 | value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ 317 | if (!value) return 0; 318 | 319 | while (*value==',') 320 | { 321 | cJSON *new_item; 322 | if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ 323 | child->next=new_item;new_item->prev=child;child=new_item; 324 | value=skip(parse_value(child,skip(value+1))); 325 | if (!value) return 0; /* memory fail */ 326 | } 327 | 328 | if (*value==']') return value+1; /* end of array */ 329 | ep=value;return 0; /* malformed. */ 330 | } 331 | 332 | /* Render an array to text */ 333 | static char *print_array(cJSON *item,int depth,int fmt) 334 | { 335 | char **entries; 336 | char *out=0,*ptr,*ret;int len=5; 337 | cJSON *child=item->child; 338 | int numentries=0,i=0,fail=0; 339 | 340 | /* How many entries in the array? */ 341 | while (child) numentries++,child=child->next; 342 | /* Allocate an array to hold the values for each */ 343 | entries=(char**)cJSON_malloc(numentries*sizeof(char*)); 344 | if (!entries) return 0; 345 | memset(entries,0,numentries*sizeof(char*)); 346 | /* Retrieve all the results: */ 347 | child=item->child; 348 | while (child && !fail) 349 | { 350 | ret=print_value(child,depth+1,fmt); 351 | entries[i++]=ret; 352 | if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; 353 | child=child->next; 354 | } 355 | 356 | /* If we didn't fail, try to malloc the output string */ 357 | if (!fail) out=(char*)cJSON_malloc(len); 358 | /* If that fails, we fail. */ 359 | if (!out) fail=1; 360 | 361 | /* Handle failure. */ 362 | if (fail) 363 | { 364 | for (i=0;itype=cJSON_Object; 390 | value=skip(value+1); 391 | if (*value=='}') return value+1; /* empty array. */ 392 | 393 | item->child=child=cJSON_New_Item(); 394 | if (!item->child) return 0; 395 | value=skip(parse_string(child,skip(value))); 396 | if (!value) return 0; 397 | child->string=child->valuestring;child->valuestring=0; 398 | if (*value!=':') {ep=value;return 0;} /* fail! */ 399 | value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ 400 | if (!value) return 0; 401 | 402 | while (*value==',') 403 | { 404 | cJSON *new_item; 405 | if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ 406 | child->next=new_item;new_item->prev=child;child=new_item; 407 | value=skip(parse_string(child,skip(value+1))); 408 | if (!value) return 0; 409 | child->string=child->valuestring;child->valuestring=0; 410 | if (*value!=':') {ep=value;return 0;} /* fail! */ 411 | value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ 412 | if (!value) return 0; 413 | } 414 | 415 | if (*value=='}') return value+1; /* end of array */ 416 | ep=value;return 0; /* malformed. */ 417 | } 418 | 419 | /* Render an object to text. */ 420 | static char *print_object(cJSON *item,int depth,int fmt) 421 | { 422 | char **entries=0,**names=0; 423 | char *out=0,*ptr,*ret,*str;int len=7,i=0,j; 424 | cJSON *child=item->child; 425 | int numentries=0,fail=0; 426 | /* Count the number of entries. */ 427 | while (child) numentries++,child=child->next; 428 | /* Allocate space for the names and the objects */ 429 | entries=(char**)cJSON_malloc(numentries*sizeof(char*)); 430 | if (!entries) return 0; 431 | names=(char**)cJSON_malloc(numentries*sizeof(char*)); 432 | if (!names) {cJSON_free(entries);return 0;} 433 | memset(entries,0,sizeof(char*)*numentries); 434 | memset(names,0,sizeof(char*)*numentries); 435 | 436 | /* Collect all the results into our arrays: */ 437 | child=item->child;depth++;if (fmt) len+=depth; 438 | while (child) 439 | { 440 | names[i]=str=print_string_ptr(child->string); 441 | entries[i++]=ret=print_value(child,depth,fmt); 442 | if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; 443 | child=child->next; 444 | } 445 | 446 | /* Try to allocate the output string */ 447 | if (!fail) out=(char*)cJSON_malloc(len); 448 | if (!out) fail=1; 449 | 450 | /* Handle failure */ 451 | if (fail) 452 | { 453 | for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} 482 | cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} 483 | /* FGG update to support update of json objects with other json objects */ 484 | char *cJSON_GetArrayItemName(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c->string;} 485 | cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} 486 | 487 | /* Utility for array list handling. */ 488 | static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} 489 | /* Utility for handling references. */ 490 | static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} 491 | 492 | /* Add item to array/object. */ 493 | void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} 494 | void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} 495 | void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} 496 | void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} 497 | 498 | cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; 499 | if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} 500 | void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} 501 | cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} 502 | void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} 503 | 504 | /* Replace array/object items with new ones. */ 505 | void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; 506 | newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; 507 | if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} 508 | void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} 509 | 510 | /* Create basic types: */ 511 | cJSON *cJSON_CreateNull() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} 512 | cJSON *cJSON_CreateTrue() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} 513 | cJSON *cJSON_CreateFalse() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} 514 | cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} 515 | cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} 516 | cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} 517 | cJSON *cJSON_CreateArray() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} 518 | cJSON *cJSON_CreateObject() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} 519 | 520 | /* Create Arrays: */ 521 | cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} 522 | cJSON *cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} 523 | cJSON *cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} 524 | cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} 525 | -------------------------------------------------------------------------------- /src/cJSON.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009 Dave Gamble 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #ifndef cJSON__h 24 | #define cJSON__h 25 | 26 | #ifdef __cplusplus 27 | extern "C" 28 | { 29 | #endif 30 | 31 | /* cJSON Types: */ 32 | #define cJSON_False 0 33 | #define cJSON_True 1 34 | #define cJSON_NULL 2 35 | #define cJSON_Number 3 36 | #define cJSON_String 4 37 | #define cJSON_Array 5 38 | #define cJSON_Object 6 39 | 40 | #define cJSON_IsReference 256 41 | 42 | /* The cJSON structure: */ 43 | typedef struct cJSON { 44 | struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ 45 | struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ 46 | 47 | int type; /* The type of the item, as above. */ 48 | 49 | char *valuestring; /* The item's string, if type==cJSON_String */ 50 | int valueint; /* The item's number, if type==cJSON_Number */ 51 | double valuedouble; /* The item's number, if type==cJSON_Number */ 52 | 53 | char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ 54 | } cJSON; 55 | 56 | typedef struct cJSON_Hooks { 57 | void *(*malloc_fn)(size_t sz); 58 | void (*free_fn)(void *ptr); 59 | } cJSON_Hooks; 60 | 61 | /* Supply malloc, realloc and free functions to cJSON */ 62 | extern void cJSON_InitHooks(cJSON_Hooks* hooks); 63 | 64 | 65 | /* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ 66 | extern cJSON *cJSON_Parse(const char *value); 67 | /* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ 68 | extern char *cJSON_Print(cJSON *item); 69 | /* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ 70 | extern char *cJSON_PrintUnformatted(cJSON *item); 71 | /* Delete a cJSON entity and all subentities. */ 72 | extern void cJSON_Delete(cJSON *c); 73 | 74 | /* Returns the number of items in an array (or object). */ 75 | extern int cJSON_GetArraySize(cJSON *array); 76 | /* FGG update to support update of json objects with other json objects */ 77 | extern char *cJSON_GetArrayItemName(cJSON *array,int item); 78 | /* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ 79 | extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); 80 | /* Get item "string" from object. Case insensitive. */ 81 | extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); 82 | 83 | /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ 84 | extern const char *cJSON_GetErrorPtr(); 85 | 86 | /* These calls create a cJSON item of the appropriate type. */ 87 | extern cJSON *cJSON_CreateNull(); 88 | extern cJSON *cJSON_CreateTrue(); 89 | extern cJSON *cJSON_CreateFalse(); 90 | extern cJSON *cJSON_CreateBool(int b); 91 | extern cJSON *cJSON_CreateNumber(double num); 92 | extern cJSON *cJSON_CreateString(const char *string); 93 | extern cJSON *cJSON_CreateArray(); 94 | extern cJSON *cJSON_CreateObject(); 95 | 96 | /* These utilities create an Array of count items. */ 97 | extern cJSON *cJSON_CreateIntArray(int *numbers,int count); 98 | extern cJSON *cJSON_CreateFloatArray(float *numbers,int count); 99 | extern cJSON *cJSON_CreateDoubleArray(double *numbers,int count); 100 | extern cJSON *cJSON_CreateStringArray(const char **strings,int count); 101 | 102 | /* Append item to the specified array/object. */ 103 | extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); 104 | extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); 105 | /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ 106 | extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); 107 | extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); 108 | 109 | /* Remove/Detatch items from Arrays/Objects. */ 110 | extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); 111 | extern void cJSON_DeleteItemFromArray(cJSON *array,int which); 112 | extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); 113 | extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); 114 | 115 | /* Update array items. */ 116 | extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); 117 | extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); 118 | 119 | #define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) 120 | #define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) 121 | #define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) 122 | #define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) 123 | #define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) 124 | 125 | #ifdef __cplusplus 126 | } 127 | #endif 128 | 129 | #endif 130 | -------------------------------------------------------------------------------- /src/ck-crowdnode-server.c: -------------------------------------------------------------------------------- 1 | /* 2 | # ck-crowdnode 3 | # 4 | # Standalone, thin and portable server to let users participate in experiment crowdsourcing via CK 5 | # 6 | # See LICENSE.txt for licensing details. 7 | # See Copyright.txt for copyright details. 8 | # 9 | # Developer: Daniil Efremov 10 | * 11 | * Contributors: Dmitry Savenko 12 | * Grigori Fursin 13 | * 14 | */ 15 | #include 16 | #include 17 | #include 18 | 19 | #if defined(__linux__) || defined(__APPLE__) 20 | #include 21 | #include 22 | #include /* socket, connect */ 23 | #include /* struct hostent, gethostbyname */ 24 | #include /* struct sockaddr_in, struct sockaddr */ 25 | #include 26 | #include 27 | #include 28 | #include 29 | #elif _WIN32 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | struct thread_win_params { 36 | int sock; 37 | int newsock; 38 | char * baseDir; 39 | }; 40 | 41 | void doProcessingWin (struct thread_win_params* twp); 42 | 43 | #pragma comment(lib,"ws2_32.lib") //Winsock Library 44 | #else 45 | #endif 46 | 47 | #include "cJSON.h" 48 | #include "base64.h" 49 | #include "urldecoder.h" 50 | #include "net_uuid.h" 51 | 52 | #include 53 | 54 | static char *const CK_JSON_KEY = "ck_json="; 55 | 56 | static char *const JSON_PARAM_NAME_COMMAND = "action"; 57 | static char *const JSON_PARAM_PARAMS = "parameters"; 58 | static char *const JSCON_PARAM_VALUE_PUSH = "push"; 59 | static char *const JSON_PARAM_FILE_NAME = "filename"; 60 | static char *const JSON_PARAM_FILE_CONTENT = "file_content_base64"; 61 | static char *const JSON_PARAM_EXTRA_PATH = "extra_path"; 62 | static char *const JSON_PARAM_SHELL_COMMAND = "cmd"; 63 | 64 | #define MAX_BUFFER_SIZE 1024 65 | #define DEFAULT_SERVER_PORT 3333 66 | static const int MAXPENDING = 5; /* Maximum outstanding connection requests */ 67 | #define GENERATED_KEY_SIZE 8 68 | 69 | static char *const JSON_CONFIG_PARAM_PORT = "port"; 70 | static char *const JSON_CONFIG_PARAM_PATH_TO_FILES = "path_to_files"; 71 | static char *const JSON_CONFIG_PARAM_SECRET_KEY = "secret_key"; 72 | 73 | #ifdef _WIN32 74 | static char *const DEFAULT_BASE_DIR = "%LOCALAPPDATA%\\ck-crowdnode-files"; 75 | static char *const DEFAULT_CONFIG_DIR = "%LOCALAPPDATA%\\.ck-crowdnode\\"; 76 | static char *const DEFAULT_CONFIG_FILE_PATH = "%LOCALAPPDATA%\\.ck-crowdnode\\ck-crowdnode-config.json"; 77 | static char *const HOME_DIR_TEMPLATE = "%LOCALAPPDATA%"; 78 | static char *const HOME_DIR_ENV_KEY = "LOCALAPPDATA"; 79 | #define FILE_SEPARATOR "\\" 80 | #define FILE_SEPARATOR_CHAR '\\' 81 | #else 82 | static char *const DEFAULT_BASE_DIR = "$HOME/ck-crowdnode-files"; 83 | static char *const DEFAULT_CONFIG_DIR = "$HOME/.ck-crowdnode/"; 84 | static char *const DEFAULT_CONFIG_FILE_PATH = "$HOME/.ck-crowdnode/ck-crowdnode-config.json"; 85 | static char *const HOME_DIR_TEMPLATE = "$HOME"; 86 | static char *const HOME_DIR_ENV_KEY = "HOME"; 87 | #define FILE_SEPARATOR "/" 88 | #define FILE_SEPARATOR_CHAR '/' 89 | 90 | int WSAGetLastError() { 91 | return 0; 92 | } 93 | #endif 94 | 95 | /** 96 | * Input: command in CK JSON format TDB 97 | * Output: Execution result in CK JSON format 98 | * 99 | * Examples: 100 | * push command 101 | * input JSON: 102 | * {"command":"push", "parameters": {"secret_key":"", "filename":"file1", "file_content_base64":""} } 103 | * 104 | * output result JSON: 105 | * {"result":"0"} 106 | * 107 | * pull command 108 | * input JSON: 109 | * {"command":"pull", "parameters": {"secret_key":"", "filename":"file1"}} 110 | * 111 | * output result JSON: 112 | * {"result":"0", "filename":"file1", "file_content_base64":""} 113 | * 114 | * shell command 115 | * input JSON: 116 | * {"command":"shell", parameters": {"secret_key":"", "cmd":"" }} 117 | * 118 | * output result JSON: 119 | * {"return":"0","return_code":256,"encoding":"UTF-8","stdout_base64":"","stderr_base64":"Y2F0OiBydXMudHh0OiBObyBzdWNoIGZpbGUgb3IgZGlyZWN0b3J5Cg=="} 120 | * 121 | * state command 122 | * input JSON: 123 | * {"return":"0", "parameters":{"secret_key":""}} 124 | * 125 | * output result JSON: 126 | * {"path_to_files":"/home/user/ck-crowdnode-files"} 127 | * 128 | * todo list: 129 | * - Check/Implement concurrent execution - looks like thread fors well at linus and windows as well 130 | * - asynch checll command execution 131 | */ 132 | 133 | void doProcessing(int sock, char *baseDir); 134 | 135 | int sockSend(int sock, const void* buf, size_t len) { 136 | #ifdef _WIN32 137 | return send(sock, buf, len, 0); 138 | #else 139 | return write(sock, buf, len); 140 | #endif 141 | } 142 | 143 | int sockSendAll(int sock, const void* buf, size_t len) { 144 | const char* p = buf; 145 | while (0 < len) { 146 | int n = sockSend(sock, p, len); 147 | if (0 >= n) { 148 | return -1; 149 | } 150 | p += n; 151 | len -= n; 152 | } 153 | return 0; 154 | } 155 | 156 | int sendHttpResponse(int sock, int httpStatus, char* payload, int size) { 157 | // send HTTP headers 158 | char buf[300]; 159 | int n = sprintf(buf, "HTTP/1.1 %d OK\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: %d\r\n\r\n", httpStatus, size); 160 | if (0 >= n) { 161 | perror("sprintf failed"); 162 | return -1; 163 | } 164 | if (0 > sockSendAll(sock, buf, n)) { 165 | perror("Failed to send HTTP response headers"); 166 | return -1; 167 | } 168 | 169 | // send payload 170 | if (0 > sockSendAll(sock, payload, size)) { 171 | perror("Failed to send HTTP response body"); 172 | return -1; 173 | } 174 | 175 | return 0; 176 | } 177 | 178 | void sendErrorMessage(int sock, char * errorMessage, const char *errorCode) { 179 | perror(errorMessage); 180 | 181 | cJSON *resultJSON = cJSON_CreateObject(); 182 | if (!resultJSON) { 183 | perror("[ERROR]: resultJSON cannot be created"); 184 | return; 185 | } 186 | 187 | cJSON_AddItemToObject(resultJSON, "return", cJSON_CreateString(errorCode)); 188 | cJSON_AddItemToObject(resultJSON, "error", cJSON_CreateString(errorMessage)); 189 | char *resultJSONtext = cJSON_PrintUnformatted(resultJSON); 190 | if (!resultJSONtext) { 191 | perror("[ERROR]: resultJSONtext cannot be created"); 192 | return; 193 | } 194 | int n = sendHttpResponse(sock, 200, resultJSONtext, strlen(resultJSONtext)); 195 | if (n < 0) { 196 | perror("ERROR writing to socket"); 197 | return ; 198 | } 199 | free(resultJSONtext); 200 | cJSON_Delete(resultJSON); 201 | } 202 | 203 | char* concat(const char *str1, const char *str2) { 204 | size_t totalSize = strlen(str1) + strlen(str2) + sizeof(char); 205 | char *message = malloc(totalSize); 206 | memset(message, 0, totalSize); 207 | 208 | if(!message){ 209 | printf("[ERROR]: Memory not allocated for concat\n"); 210 | exit(-1); 211 | } 212 | 213 | strcat(message, str1); 214 | strcat(message + strlen(str1), str2); 215 | return message; 216 | } 217 | 218 | void dieWithError(char *error) { 219 | printf("Connection error: %s %i", error, WSAGetLastError()); 220 | exit(1); 221 | } 222 | 223 | typedef struct { 224 | int port; 225 | char *pathToFiles; 226 | char *secretKey; 227 | 228 | } CKCrowdnodeServerConfig; 229 | 230 | CKCrowdnodeServerConfig *ckCrowdnodeServerConfig; 231 | char *serverSecretKey; 232 | 233 | 234 | static char *const JSON_PARAM_NAME_SECRETKEY = "secretkey"; 235 | static char *const ERROR_MESSAGE_SECRET_KEY_MISSMATCH = "secret keys do not match"; 236 | static char *const ERROR_CODE_SECRET_KEY_MISMATCH = "3"; 237 | static char *const ERROR_CODE = "1"; 238 | 239 | static const int DEFAULT_DIR_MODE = 0700; 240 | 241 | char *str_replace(char *orig, char *rep, char *with) { 242 | char *result; // the return string 243 | char *ins; // the next insert point 244 | char *tmp; // varies 245 | int len_rep; // length of rep 246 | int len_with; // length of with 247 | int len_front; // distance between rep and end of last rep 248 | int count; // number of replacements 249 | 250 | if (!orig) 251 | return NULL; 252 | if (!rep) 253 | rep = ""; 254 | len_rep = strlen(rep); 255 | if (!with) 256 | with = ""; 257 | len_with = strlen(with); 258 | 259 | ins = orig; 260 | for (count = 0; (tmp = strstr(ins, rep)); ++count) { 261 | ins = tmp + len_rep; 262 | } 263 | 264 | // first time through the loop, all the variable are set correctly 265 | // from here on, 266 | // tmp points to the end of the result string 267 | // ins points to the next occurrence of rep in orig 268 | // orig points to the remainder of orig after "end of rep" 269 | tmp = result = malloc(strlen(orig) + (len_with - len_rep) * count + 1); 270 | 271 | if (!result) 272 | return NULL; 273 | 274 | while (count--) { 275 | ins = strstr(orig, rep); 276 | len_front = ins - orig; 277 | tmp = strncpy(tmp, orig, len_front) + len_front; 278 | tmp = strcpy(tmp, with) + len_with; 279 | orig += len_front + len_rep; // move to next "end of rep" 280 | } 281 | strcpy(tmp, orig); 282 | return result; 283 | } 284 | 285 | char *getEnvValue(char *param, char** envp ) { 286 | char * value; 287 | while (*envp) { 288 | if (strstr(*envp, param) != NULL) { 289 | value = malloc(strlen(*envp) + 1); 290 | if (!value) { 291 | perror("[ERROR]: Memory not allocated for getEnvValue"); 292 | return NULL; 293 | } 294 | strcpy(value, *envp); 295 | char *rep = concat(param, "="); 296 | char *string = str_replace(value, rep, ""); 297 | return string; 298 | } 299 | ++envp; 300 | } 301 | return NULL; 302 | } 303 | 304 | 305 | char* getAbsolutePath(char *pathToFiles, char** envp) { 306 | size_t size = strlen(pathToFiles) + sizeof(char); 307 | char * absolutePath = malloc(size); 308 | memset(absolutePath, 0, size); 309 | strcpy(absolutePath, pathToFiles); 310 | if (strstr(absolutePath, HOME_DIR_TEMPLATE) != NULL) { 311 | return str_replace(absolutePath, HOME_DIR_TEMPLATE, getEnvValue(HOME_DIR_ENV_KEY, envp)); 312 | } 313 | return absolutePath; 314 | } 315 | 316 | int loadConfigFromFile(CKCrowdnodeServerConfig *ckCrowdnodeServerConfig, char** envp) { 317 | char *filePath = getAbsolutePath(DEFAULT_CONFIG_FILE_PATH, envp); 318 | 319 | FILE *file=fopen(filePath, "rb"); 320 | if (!file) { 321 | printf("[WARN]: File not found at path: %s\n", filePath); 322 | return 0; 323 | } 324 | 325 | fseek(file, 0, SEEK_END); 326 | long fsize = ftell(file); 327 | fseek(file, 0, SEEK_SET); 328 | 329 | char *fileContent = malloc(fsize + 1); 330 | memset(fileContent,0, fsize + 1); 331 | fread(fileContent, fsize, 1, file); 332 | fclose(file); 333 | 334 | cJSON *configSON = cJSON_Parse(fileContent); 335 | if (!configSON) { 336 | printf("[ERROR]: Invalid JSON format for configuration file %s\n", filePath); 337 | return 0; 338 | } 339 | 340 | cJSON *portJSON= cJSON_GetObjectItem(configSON, JSON_CONFIG_PARAM_PORT); 341 | if (!portJSON) { 342 | printf("[ERROR]: Invalid JSON format for provided message, attribute %s not found\n", JSON_CONFIG_PARAM_PORT); 343 | if (configSON != NULL) { 344 | cJSON_Delete(configSON); 345 | } 346 | return 0; 347 | } 348 | int port = portJSON->valueint; 349 | ckCrowdnodeServerConfig->port =port; 350 | 351 | cJSON *pathSON = cJSON_GetObjectItem(configSON, JSON_CONFIG_PARAM_PATH_TO_FILES); 352 | if (!pathSON) { 353 | printf("[ERROR]: Invalid JSON format for provided message, attribute %s not found\n", JSON_CONFIG_PARAM_PATH_TO_FILES); 354 | if (configSON != NULL) { 355 | cJSON_Delete(configSON); 356 | } 357 | return 0; 358 | } 359 | char *pathToFiles = getAbsolutePath(pathSON->valuestring, envp); 360 | ckCrowdnodeServerConfig->pathToFiles = strdup(pathToFiles); 361 | 362 | char * secretKey; 363 | cJSON *secretKeyJSON = cJSON_GetObjectItem(configSON, JSON_CONFIG_PARAM_SECRET_KEY); 364 | if (!secretKeyJSON) { 365 | printf("[ERROR]: Invalid JSON format for provided message, attribute %s not found\n", JSON_CONFIG_PARAM_SECRET_KEY); 366 | if (configSON != NULL) { 367 | cJSON_Delete(configSON); 368 | } 369 | return 0; 370 | } else { 371 | secretKey = secretKeyJSON->valuestring; 372 | } 373 | size_t size = strlen(secretKey) + sizeof(char); 374 | ckCrowdnodeServerConfig->secretKey = malloc(size); 375 | memset(ckCrowdnodeServerConfig->secretKey, 0, size); 376 | strcpy(ckCrowdnodeServerConfig->secretKey, secretKey); 377 | cJSON_Delete(configSON); 378 | return 1; 379 | } 380 | 381 | void createCKFilesDirectoryIfDoesnotExist(const char *dir) { 382 | char *p = NULL; 383 | size_t len; 384 | 385 | char *tmp = strdup(dir); 386 | len = strlen(tmp); 387 | if(tmp[len - 1] == FILE_SEPARATOR_CHAR) 388 | tmp[len - 1] = 0; 389 | for(p = tmp + 1; *p; p++) 390 | if(*p == FILE_SEPARATOR_CHAR) { 391 | *p = 0; 392 | mkdir(tmp, DEFAULT_DIR_MODE); 393 | *p = FILE_SEPARATOR_CHAR; 394 | } 395 | mkdir(tmp, DEFAULT_DIR_MODE); 396 | free(tmp); 397 | } 398 | 399 | char * generateKey() { 400 | int uuidSize = DEFAULT_UUID_SIZE; 401 | if (GENERATED_KEY_SIZE > DEFAULT_UUID_SIZE) { 402 | uuidSize = GENERATED_KEY_SIZE; 403 | } 404 | char * generatedSecretKey = malloc(uuidSize + sizeof(char)); 405 | get_uuid_string(generatedSecretKey, uuidSize); 406 | char *secretKey = malloc(GENERATED_KEY_SIZE + sizeof(char)); 407 | memset(secretKey, 0, GENERATED_KEY_SIZE); 408 | strncpy(secretKey, generatedSecretKey, GENERATED_KEY_SIZE); 409 | secretKey[GENERATED_KEY_SIZE] = '\0'; 410 | return secretKey; 411 | } 412 | 413 | void loadDefaultConfig(CKCrowdnodeServerConfig *ckCrowdnodeServerConfig, char** envp) { 414 | ckCrowdnodeServerConfig->port = DEFAULT_SERVER_PORT; 415 | ckCrowdnodeServerConfig->pathToFiles = getAbsolutePath(DEFAULT_BASE_DIR, envp); 416 | ckCrowdnodeServerConfig->secretKey = generateKey(); 417 | 418 | char *configDir = getAbsolutePath(DEFAULT_CONFIG_DIR, envp); 419 | int createDirState = 0; 420 | createDirState = mkdir(configDir, DEFAULT_DIR_MODE); 421 | if (createDirState<0) { 422 | perror("[WARN]: Configuration directory was not created"); 423 | } 424 | 425 | char *configFilePath = getAbsolutePath(DEFAULT_CONFIG_FILE_PATH, envp); 426 | FILE *file = fopen(configFilePath, "wb"); 427 | if (!file) { 428 | perror("[ERROR]: Could not created default configuration file\n"); 429 | exit(1); 430 | } 431 | 432 | printf("[DEBUG]: Open default configuration file to write %s\n", configFilePath); 433 | 434 | cJSON *defaultConfigJSON = cJSON_CreateObject(); 435 | if (!defaultConfigJSON) { 436 | perror("[ERROR]: Memory not allocated for defaultConfigJSON\n"); 437 | exit(1); 438 | } 439 | 440 | char *defaultCrowdnodeServerConfig = strdup(ckCrowdnodeServerConfig->secretKey); 441 | cJSON_AddNumberToObject(defaultConfigJSON, JSON_CONFIG_PARAM_PORT, DEFAULT_SERVER_PORT); 442 | cJSON_AddItemToObject(defaultConfigJSON, JSON_CONFIG_PARAM_PATH_TO_FILES, cJSON_CreateString(getAbsolutePath(DEFAULT_BASE_DIR, envp))); 443 | cJSON_AddItemToObject(defaultConfigJSON, JSON_CONFIG_PARAM_SECRET_KEY, cJSON_CreateString(defaultCrowdnodeServerConfig)); 444 | char *file_content = cJSON_PrintUnformatted(defaultConfigJSON); 445 | printf("[INFO]: Default configuration JSON created: %s\n", file_content); 446 | 447 | int results = fwrite(file_content, 1, strlen(file_content), file); 448 | if (results == EOF) { 449 | perror("[ERROR]: Failed to write default configuration file"); 450 | exit(1); 451 | 452 | } 453 | fclose(file); 454 | free(file_content); 455 | cJSON_Delete(defaultConfigJSON); 456 | } 457 | 458 | char *getLocalIPv4Adress() { 459 | char * ip= malloc(NI_MAXHOST + 1); 460 | if (!ip) { 461 | perror("[ERROR]: Could not allocate memory for ip address string"); 462 | } 463 | #ifdef _WIN32 464 | WSADATA wsaData; 465 | char name[255]; 466 | PHOSTENT hostinfo; 467 | if ( WSAStartup( MAKEWORD( 2, 0 ), &wsaData ) == 0 ) { 468 | 469 | if( gethostname ( name, sizeof(name)) == 0) { 470 | if((hostinfo = gethostbyname(name)) != NULL) { 471 | ip = inet_ntoa (*(struct in_addr *)*hostinfo->h_addr_list); 472 | } 473 | } 474 | 475 | WSACleanup( ); 476 | } 477 | #else 478 | struct ifaddrs *ifaddr, *ifa; 479 | int family, s; 480 | if (getifaddrs(&ifaddr) == -1) { 481 | perror("getifaddrs"); 482 | exit(EXIT_FAILURE); 483 | } 484 | for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { 485 | if (ifa->ifa_addr == NULL) 486 | continue; 487 | 488 | s=getnameinfo(ifa->ifa_addr,sizeof(struct sockaddr_in),ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); 489 | 490 | if((strcmp(ifa->ifa_name,"wlan0")==0)&&(ifa->ifa_addr->sa_family==AF_INET)) { 491 | if (s != 0) { 492 | printf("getnameinfo() failed: %s\n", gai_strerror(s)); 493 | exit(EXIT_FAILURE); 494 | } 495 | printf("\tInterface : <%s>\n",ifa->ifa_name ); 496 | printf("\t Address : <%s>\n", ip); 497 | } 498 | } 499 | freeifaddrs(ifaddr); 500 | #endif 501 | return ip; 502 | } 503 | 504 | char * getStdoutEncoding() { 505 | setlocale (LC_ALL, ""); 506 | char *currentLocale = setlocale(LC_ALL, NULL); 507 | char *encoding; 508 | char *encodingWithDot = strstr(currentLocale, "."); 509 | if (encodingWithDot != NULL) { 510 | encoding = encodingWithDot + sizeof(char); 511 | return strdup(encoding); 512 | } 513 | return NULL; 514 | } 515 | 516 | int main( int argc, char *argv[] , char** envp) { 517 | 518 | printf("[INFO]: CK-crowdnode-server starting ...\n"); 519 | printf("[INFO]: Server default encoding: %s\n", getStdoutEncoding()); 520 | printf("[INFO]: %s env value: %s\n", HOME_DIR_TEMPLATE, getEnvValue(HOME_DIR_ENV_KEY, envp)); 521 | printf("[INFO]: Configuration file absolute path: %s\n", getAbsolutePath(DEFAULT_CONFIG_FILE_PATH, envp)); 522 | ckCrowdnodeServerConfig = malloc(sizeof(CKCrowdnodeServerConfig)); 523 | if (!ckCrowdnodeServerConfig) { 524 | perror("[ERROR]: Memory not allocated for ckCrowdnodeServerConfig\n"); 525 | exit(1); 526 | } 527 | 528 | if (!loadConfigFromFile(ckCrowdnodeServerConfig, envp)) { 529 | loadDefaultConfig(ckCrowdnodeServerConfig, envp); 530 | printf("[WARN]: CK-crowdnode-server configuration file problem. Server will be started with default configuration\n"); 531 | } else { 532 | printf("[INFO]: CK-crowdnode-server configuration file loaded successfully with configuration\n"); 533 | } 534 | 535 | printf("\n"); 536 | printf("[INFO for CK client]: server real IP: %s\n", getLocalIPv4Adress()); 537 | printf("[INFO for CK client]: server port: %i\n", ckCrowdnodeServerConfig->port); 538 | printf("[INFO for CK client]: server path to files: %s\n", ckCrowdnodeServerConfig->pathToFiles); 539 | printf("[INFO for CK client]: secret key: %s\n", ckCrowdnodeServerConfig->secretKey); 540 | printf("\n"); 541 | 542 | createCKFilesDirectoryIfDoesnotExist(getAbsolutePath(ckCrowdnodeServerConfig->pathToFiles, envp)); 543 | 544 | serverSecretKey = ckCrowdnodeServerConfig->secretKey; 545 | int sockfd, newsockfd; 546 | socklen_t clilen; 547 | int portno = ckCrowdnodeServerConfig->port; 548 | char *baseDir = malloc(strlen(ckCrowdnodeServerConfig->pathToFiles) * sizeof(char) + 1); 549 | if (!baseDir) { 550 | perror("Could not allocate memory for baseDir"); 551 | exit(1); 552 | } 553 | strcpy(baseDir, ckCrowdnodeServerConfig->pathToFiles); 554 | unsigned long win_thread_id; 555 | 556 | #ifdef _WIN32 557 | struct thread_win_params twp; 558 | struct thread_win_params* ptwp=&twp; 559 | #endif 560 | 561 | struct sockaddr_in serv_addr, cli_addr; 562 | 563 | #ifdef _WIN32 564 | int servSock; /* Socket descriptor for server */ 565 | int clntSock; /* Socket descriptor for client */ 566 | struct sockaddr_in echoServAddr; /* Local address */ 567 | struct sockaddr_in echoClntAddr; /* Client address */ 568 | unsigned short echoServPort; /* Server port */ 569 | unsigned int clntLen; /* Length of client address data structure */ 570 | WSADATA wsaData; /* Structure for WinSock setup communication */ 571 | 572 | echoServPort = DEFAULT_SERVER_PORT; 573 | 574 | if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) /* Load Winsock 2.0 DLL */ 575 | { 576 | fprintf(stderr, "WSAStartup() failed"); 577 | exit(1); 578 | } 579 | 580 | /* Create socket for incoming connections */ 581 | if ((servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { 582 | dieWithError("socket() failed"); 583 | } 584 | 585 | /* Construct local address structure */ 586 | memset(&echoServAddr, 0, sizeof(echoServAddr)); /* Zero out structure */ 587 | echoServAddr.sin_family = AF_INET; /* Internet address family */ 588 | echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ 589 | echoServAddr.sin_port = htons(echoServPort); /* Local port */ 590 | 591 | /* Bind to the local address */ 592 | if (bind(servSock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0) { 593 | dieWithError("bind() failed"); 594 | } 595 | 596 | /* Mark the socket so it will listen for incoming connections */ 597 | if (listen(servSock, MAXPENDING) < 0) { 598 | dieWithError("listen() failed"); 599 | } 600 | 601 | #else 602 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 603 | 604 | if (sockfd < 0) { 605 | perror("ERROR opening socket"); 606 | printf("WSAGetLastError() %i\n", WSAGetLastError()); //win 607 | exit(1); 608 | } 609 | 610 | memset((char *) &serv_addr, 0, sizeof(serv_addr)); 611 | 612 | serv_addr.sin_family = AF_INET; 613 | serv_addr.sin_addr.s_addr = INADDR_ANY; 614 | serv_addr.sin_port = htons(portno); 615 | 616 | if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { 617 | perror("ERROR on binding"); 618 | exit(1); 619 | } 620 | printf("[INFO]: Server started at port %i\n", portno); 621 | 622 | listen(sockfd,5); 623 | clilen = sizeof(cli_addr); 624 | #endif 625 | 626 | 627 | /** 628 | * Main server loop 629 | */ 630 | while (1) { 631 | 632 | /** 633 | * Create child process 634 | */ 635 | #ifdef _WIN32 636 | /* Set the size of the in-out parameter */ 637 | clntLen = sizeof(echoClntAddr); 638 | 639 | /* Wait for a client to connect */ 640 | printf("[INFO] CK-crowdnode-server listen commands on port %i\n", portno); 641 | if ((clntSock = accept(servSock, (struct sockaddr *) &echoClntAddr, &clntLen)) < 0) { 642 | dieWithError("accept() failed"); 643 | } 644 | 645 | ptwp->sock=servSock; 646 | ptwp->newsock=clntSock; 647 | ptwp->baseDir=baseDir; 648 | 649 | if (!CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)doProcessingWin, 650 | (struct thread_win_params*) ptwp, 0, &win_thread_id)) 651 | { 652 | perror("ERROR on fork"); 653 | exit(1); 654 | } 655 | 656 | /* closesocket(sockfd); */ 657 | #else 658 | 659 | newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); 660 | 661 | if (newsockfd < 0) { 662 | perror("ERROR on accept"); 663 | printf("WSAGetLastError() %i\n", WSAGetLastError()); //win 664 | exit(1); 665 | } 666 | pid_t pid = fork(); 667 | 668 | if (pid < 0) { 669 | perror("ERROR on fork"); 670 | exit(1); 671 | } 672 | 673 | if (pid == 0) { 674 | close(sockfd); 675 | doProcessing(newsockfd, baseDir); 676 | exit(0); 677 | } else { 678 | close(newsockfd); 679 | } 680 | #endif 681 | } 682 | } 683 | 684 | #ifdef _WIN32 685 | void doProcessingWin (struct thread_win_params* ptwp) 686 | { 687 | int sockfd=ptwp->sock; 688 | int newsockfd=ptwp->newsock; 689 | char *baseDir = ptwp->baseDir; 690 | 691 | // Child process - talk with connected client 692 | doProcessing (newsockfd, baseDir); 693 | 694 | if (shutdown (newsockfd, 2)!=0) 695 | { 696 | perror("Error on fork"); 697 | exit(1); 698 | } 699 | 700 | closesocket(newsockfd); 701 | 702 | return; 703 | } 704 | #endif 705 | 706 | /** 707 | * Tries to detect message length by the given buffer, which contains the beginning of the message. 708 | * The buffer passed must be of at least (size+1) length. 709 | * 710 | * Returns -1, if the length is still unknown (in this case the caller must provide a bigger part of the message). 711 | * 712 | * Returns -2, if the length can never be determined, i.e. HTTP headers don't contain 'Content-Length'. 713 | * 714 | * If 0 or more is returned, it is the total size of the message (size of the headers + size of the body). 715 | */ 716 | int detectMessageLength(char* buf, int size) { 717 | buf[size] = 0; 718 | 719 | // trying to find where headers end 720 | char* s = strstr(buf, "\r\n\r\n"); 721 | int header_stop_len = 4; 722 | if (NULL == s) { 723 | s = strstr(buf, "\n\n"); 724 | header_stop_len = 2; 725 | } 726 | if (NULL == s) { 727 | return -1; 728 | } 729 | const long header_len = (s - buf) + header_stop_len; 730 | 731 | const char* content_len_key = "Content-Length:"; 732 | // trying to find Content-Length 733 | char* content_len_header = strstr(buf, content_len_key); 734 | if (NULL == content_len_header || (content_len_header - buf) >= header_len) { 735 | return -2; 736 | } 737 | 738 | long l = strtol(content_len_header + strlen(content_len_key), NULL, 10); 739 | return header_len + l; 740 | } 741 | 742 | void sendJson(int sock, cJSON* json) { 743 | char* txt = cJSON_PrintUnformatted(json); 744 | if (NULL == txt) { 745 | perror("Failed to convert JSON to string"); 746 | exit(1); 747 | } 748 | int n1 = sendHttpResponse(sock, 200, txt, strlen(txt)); 749 | free(txt); 750 | if (n1 < 0) { 751 | perror("ERROR sending JSON to socket"); 752 | } 753 | } 754 | 755 | void processPush(int sock, char* baseDir, cJSON* commandJSON) { 756 | // push file (to send file to CK Node ) 757 | cJSON *filenameJSON = cJSON_GetObjectItem(commandJSON, JSON_PARAM_FILE_NAME); 758 | if (!filenameJSON) { 759 | printf("[ERROR]: Invalid action JSON format for provided message\n"); 760 | sendErrorMessage(sock, "Invalid action JSON format for message: no filenameJSON found", ERROR_CODE); 761 | return; 762 | } 763 | char *fileName = filenameJSON->valuestring; 764 | printf("[DEBUG]: File name: %s\n", fileName); 765 | 766 | char *finalBaseDir = concat(baseDir, FILE_SEPARATOR); 767 | 768 | // Optional param extra_path 769 | cJSON *extraPathJSON = cJSON_GetObjectItem(commandJSON, JSON_PARAM_EXTRA_PATH); 770 | char *extraPath = ""; 771 | if (extraPathJSON) { 772 | extraPath = extraPathJSON->valuestring; 773 | printf("[INFO]: Extra path provided: %s\n", extraPath); 774 | 775 | finalBaseDir = concat(finalBaseDir, extraPath); 776 | finalBaseDir = concat(finalBaseDir, FILE_SEPARATOR); 777 | createCKFilesDirectoryIfDoesnotExist(finalBaseDir); 778 | } 779 | 780 | cJSON *fileContentJSON = cJSON_GetObjectItem(commandJSON, JSON_PARAM_FILE_CONTENT); 781 | if (!fileContentJSON) { 782 | printf("[ERROR]: Invalid action JSON format for message: \n"); 783 | sendErrorMessage(sock, "Invalid action JSON format for message: no fileContentJSON found", ERROR_CODE); 784 | return; 785 | } 786 | char *file_content_base64 = fileContentJSON->valuestring; 787 | printf("[DEBUG]: File content base64 length: %lu\n", (unsigned long) strlen(file_content_base64)); 788 | 789 | int targetSize = ((unsigned long) strlen(file_content_base64) + 1) * 4 / 3; 790 | unsigned char *file_content = malloc(targetSize); 791 | 792 | int bytesDecoded = 0; 793 | if (strlen(file_content_base64) != 0) { 794 | bytesDecoded = base64_decode(file_content_base64, file_content, targetSize); 795 | if (bytesDecoded == 0) { 796 | sendErrorMessage(sock, "Failed to Base64 decode file", ERROR_CODE); 797 | } 798 | file_content[bytesDecoded] = '\0'; 799 | printf("[INFO]: Bytes decoded: %i\n", bytesDecoded); 800 | } else { 801 | printf("[WARNING]: file content is empty nothing to decode\n"); 802 | } 803 | 804 | char *filePath = concat(finalBaseDir,fileName); 805 | 806 | FILE *file = fopen(filePath, "wb"); 807 | if (!file) { 808 | char *message = concat("Could not write file at path: ", filePath); 809 | printf("[ERROR]: %s\n", message); 810 | sendErrorMessage(sock, message, ERROR_CODE); 811 | free(file_content); 812 | return; 813 | } 814 | 815 | printf("[DEBUG]: Open file to write %s\n", filePath); 816 | printf("[DEBUG]: Bytes to write %i\n", bytesDecoded); 817 | int results = fwrite(file_content, 1, bytesDecoded, file); 818 | if (results == EOF) { 819 | sendErrorMessage(sock, "Failed to write file ", ERROR_CODE); 820 | } 821 | fclose(file); 822 | free(file_content); 823 | printf("[INFO]: File saved to: %s\n", filePath); 824 | 825 | /** 826 | * return successful response message, example: 827 | * {"return":0, "compileUUID": } 828 | */ 829 | cJSON *resultJSON = cJSON_CreateObject(); 830 | if (!resultJSON) { 831 | perror("[ERROR]: Memory not allocated for resultJSON"); 832 | exit(1); 833 | } 834 | printf("[INFO]: resultJSON created\n"); 835 | cJSON_AddItemToObject(resultJSON, "return", cJSON_CreateString("0")); 836 | sendJson(sock, resultJSON); 837 | cJSON_Delete(resultJSON); 838 | } 839 | 840 | void processPull(int sock, char* baseDir, cJSON* commandJSON) { 841 | // pull file (to receive file from CK node) 842 | cJSON *filenameJSON = cJSON_GetObjectItem(commandJSON, JSON_PARAM_FILE_NAME); 843 | if (!filenameJSON) { 844 | printf("[ERROR]: Invalid action JSON format for provided message\n"); 845 | sendErrorMessage(sock, "Invalid action JSON format for message: no filenameJSON found", ERROR_CODE); 846 | return; 847 | } 848 | 849 | char *fileName = filenameJSON->valuestring; 850 | printf("[DEBUG]: File name: %s\n", fileName); 851 | 852 | char *finalBaseDir = concat(baseDir,FILE_SEPARATOR); 853 | 854 | // Optional param extra_path 855 | cJSON *extraPathJSON = cJSON_GetObjectItem(commandJSON, JSON_PARAM_EXTRA_PATH); 856 | char *extraPath = ""; 857 | if (extraPathJSON) { 858 | extraPath = extraPathJSON->valuestring; 859 | printf("[INFO]: Extra path provided: %s\n", extraPath); 860 | 861 | finalBaseDir = concat(finalBaseDir, extraPath); 862 | finalBaseDir = concat(finalBaseDir, FILE_SEPARATOR); 863 | } 864 | 865 | char *filePath = concat(finalBaseDir, fileName); 866 | printf("[DEBUG]: Reading file: %s\n", filePath); 867 | FILE *file = fopen(filePath, "rb"); 868 | if (!file) { 869 | char *message = concat("File not found at path:", filePath); 870 | printf("[ERROR]: %s", message); 871 | sendErrorMessage(sock, message, ERROR_CODE); 872 | return; 873 | } 874 | 875 | fseek(file, 0, SEEK_END); 876 | long fsize = ftell(file); 877 | fseek(file, 0, SEEK_SET); 878 | 879 | unsigned char *fileContent = malloc(fsize + 1); 880 | memset(fileContent, 0, fsize + 1); 881 | fread(fileContent, fsize, 1, file); 882 | fclose(file); 883 | 884 | fileContent[fsize] = 0; 885 | printf("[DEBUG]: File size: %lu\n", fsize); 886 | 887 | unsigned long targetSize = (unsigned long) ((fsize) * 4 / 3 + 5); 888 | printf("[DEBUG]: Target encoded size: %lu\n", targetSize); 889 | char *encodedContent = malloc(targetSize); 890 | if (!encodedContent) { 891 | perror("[ERROR]: Memory not allocated for encodedContent"); 892 | exit(1); 893 | } 894 | 895 | if (fsize > 0) { 896 | base64_encode(fileContent, fsize, encodedContent, targetSize); 897 | } 898 | 899 | /** 900 | * return successful response message, example: 901 | * {"return":0, "filename": , "file_content_base64":} 902 | */ 903 | cJSON *resultJSON = cJSON_CreateObject(); 904 | if (!resultJSON) { 905 | perror("[ERROR]: Memory not allocated for resultJSON"); 906 | exit(1); 907 | } 908 | cJSON_AddItemToObject(resultJSON, "return", cJSON_CreateString("0")); 909 | cJSON_AddItemToObject(resultJSON, JSON_PARAM_FILE_NAME, cJSON_CreateString(fileName)); 910 | cJSON_AddItemToObject(resultJSON, JSON_PARAM_FILE_CONTENT, cJSON_CreateString(encodedContent)); 911 | sendJson(sock, resultJSON); 912 | cJSON_Delete(resultJSON); 913 | free(encodedContent); 914 | } 915 | 916 | void processShell(int sock, cJSON* commandJSON, char *baseDir) { 917 | // shell (to execute a shell cmd from request at CK node) 918 | // todo: in future could be implemented as async process 919 | 920 | cJSON *shellCommandJSON = cJSON_GetObjectItem(commandJSON, JSON_PARAM_SHELL_COMMAND); 921 | if (!shellCommandJSON) { 922 | printf("[ERROR]: Invalid action JSON format for provided message\n"); 923 | sendErrorMessage(sock, "Invalid action JSON format for message: no filenameJSON found", ERROR_CODE); 924 | return; 925 | } 926 | 927 | char *shellCommand = shellCommandJSON->valuestring; 928 | 929 | if (!shellCommand) { 930 | printf("[ERROR]: Invalid action JSON format for provided message\n"); 931 | sendErrorMessage(sock, "Invalid action JSON format for message: no filenameJSON found", ERROR_CODE); 932 | return; 933 | } 934 | 935 | chdir(baseDir); 936 | 937 | int systemReturnCode = 0; 938 | 939 | char path[MAX_BUFFER_SIZE + 1]; 940 | unsigned char *stdoutText = malloc(MAX_BUFFER_SIZE + 1); 941 | if (stdoutText == NULL) { 942 | perror("[ERROR]: Memory not allocated for stdoutText first time"); 943 | exit(1); 944 | } 945 | memset(stdoutText, 0, MAX_BUFFER_SIZE + 1); 946 | 947 | char tmpFilename[DEFAULT_UUID_SIZE]; 948 | get_uuid_string(tmpFilename, sizeof(tmpFilename)); 949 | 950 | char *tmpStdErrFilePath = concat(baseDir, FILE_SEPARATOR); 951 | tmpStdErrFilePath = concat(tmpStdErrFilePath,tmpFilename); 952 | char *redirectString = concat(" 2>", tmpStdErrFilePath); 953 | char *shellCommandWithStdErr = concat(shellCommand, redirectString); 954 | printf("[INFO]: Run command: %s\n", shellCommandWithStdErr); 955 | /* Open the command for reading. */ 956 | FILE *fp; 957 | #ifdef _WIN32 958 | fp = _popen(shellCommandWithStdErr, "r"); 959 | #else 960 | fp = popen(shellCommandWithStdErr, "r"); 961 | #endif 962 | if (fp == NULL) { 963 | printf("[ERROR]: Failed to run command: %s\n", shellCommand); 964 | exit(1); 965 | } 966 | 967 | int totalRead = 0; 968 | while (fgets(path, sizeof(path) - 1, fp) != NULL) { 969 | unsigned long pathSize = (unsigned long)(strlen(path)); 970 | stdoutText = realloc(stdoutText, totalRead + pathSize + 1); 971 | if (stdoutText == NULL) { 972 | perror("[ERROR]: Memory not allocated stdout"); 973 | exit(1); 974 | } 975 | memcpy(stdoutText + totalRead, path, pathSize); 976 | totalRead = totalRead + pathSize; 977 | } 978 | 979 | #ifdef _WIN32 980 | systemReturnCode = _pclose(fp); 981 | #else 982 | systemReturnCode = pclose(fp); 983 | #endif 984 | 985 | stdoutText[totalRead] ='\0'; 986 | printf("[INFO]: total stdout length: %i\n", totalRead); 987 | printf("[DEBUG]: stdout: %s\n", stdoutText); 988 | 989 | char *encodedContent = ""; 990 | if (totalRead > 0) { 991 | unsigned long targetSize = (unsigned long) ((totalRead) * 4 / 3 + 5); 992 | printf("[DEBUG]: Target stdout encoded size: %lu\n", targetSize); 993 | encodedContent = malloc(targetSize); 994 | if (!encodedContent) { 995 | perror("[ERROR]: Memory not allocated for encoded stdout Content"); 996 | exit(1); 997 | } 998 | memset(encodedContent, 0, targetSize); 999 | base64_encode(stdoutText, totalRead, encodedContent, targetSize); 1000 | } 1001 | 1002 | free(stdoutText); 1003 | 1004 | cJSON *resultJSON = cJSON_CreateObject(); 1005 | cJSON_AddItemToObject(resultJSON, "return", cJSON_CreateString("0")); 1006 | 1007 | cJSON_AddNumberToObject(resultJSON, "return_code", systemReturnCode); 1008 | 1009 | cJSON_AddItemToObject(resultJSON, "encoding", cJSON_CreateString(getStdoutEncoding())); 1010 | cJSON_AddItemToObject(resultJSON, "stdout_base64", cJSON_CreateString(encodedContent)); 1011 | 1012 | long fsize = 0; 1013 | FILE *stdErrFile = fopen(tmpStdErrFilePath, "rb"); 1014 | if (!stdErrFile) { 1015 | sendErrorMessage(sock, "can't find stderr tmp file", ERROR_CODE); 1016 | return; 1017 | } 1018 | 1019 | unsigned char *stdErr = ""; 1020 | if (stdErrFile) { 1021 | fseek(stdErrFile, 0, SEEK_END); 1022 | fsize = ftell(stdErrFile); 1023 | fseek(stdErrFile, 0, SEEK_SET); 1024 | 1025 | stdErr = malloc(fsize + 1); 1026 | memset(stdErr, 0, fsize + 1); 1027 | fread(stdErr, fsize, 1, stdErrFile); 1028 | fclose(stdErrFile); 1029 | } 1030 | printf("[DEBUG]: stderr file size: %lu\n", fsize); 1031 | 1032 | char *encodedStdErr = ""; 1033 | if (fsize > 0) { 1034 | unsigned long targetSize = (unsigned long) ((fsize) * 4 / 3 + 5); 1035 | printf("[DEBUG]: Target stderr encoded size: %lu\n", targetSize); 1036 | encodedStdErr = malloc(targetSize); 1037 | if (!encodedStdErr) { 1038 | perror("[ERROR]: Memory not allocated for encoded stderr content\n"); 1039 | exit(1); 1040 | } 1041 | memset(encodedStdErr, 0, targetSize); 1042 | base64_encode(stdErr, fsize, encodedStdErr, targetSize); 1043 | free(stdErr); 1044 | } 1045 | 1046 | cJSON_AddItemToObject(resultJSON, "stderr_base64", cJSON_CreateString(encodedStdErr)); 1047 | 1048 | sendJson(sock, resultJSON); 1049 | cJSON_Delete(resultJSON); 1050 | 1051 | int ret = remove(tmpStdErrFilePath); 1052 | if(ret == 0) { 1053 | printf("[INFO]: tmp stderr file %s deleted successfully\n", tmpStdErrFilePath); 1054 | } else { 1055 | perror("[ERROR]: unable to delete the tmp stderr file\n"); 1056 | } 1057 | } 1058 | 1059 | void processState(int sock, const char *baseDir) { 1060 | cJSON *resultJSON = cJSON_CreateObject(); 1061 | cJSON_AddItemToObject(resultJSON, "return", cJSON_CreateString("0")); 1062 | cJSON *cfgJSON = cJSON_CreateObject(); 1063 | cJSON_AddItemToObject(cfgJSON, JSON_CONFIG_PARAM_PATH_TO_FILES, cJSON_CreateString(strdup(baseDir))); 1064 | cJSON_AddItemToObject(resultJSON, "cfg", cfgJSON); 1065 | sendJson(sock, resultJSON); 1066 | cJSON_Delete(resultJSON); 1067 | } 1068 | 1069 | void doProcessing(int sock, char *baseDir) { 1070 | char *client_message = malloc(MAX_BUFFER_SIZE + 1); 1071 | if (client_message == NULL) { 1072 | perror("[ERROR]: Memory not allocated for client_message first time"); 1073 | exit(1); 1074 | } 1075 | 1076 | char *buffer = malloc(MAX_BUFFER_SIZE + 1); 1077 | if (buffer == NULL) { 1078 | perror("[ERROR]: Memory not allocated buffer"); 1079 | exit(1); 1080 | } 1081 | 1082 | memset(buffer, 0, MAX_BUFFER_SIZE); 1083 | int buffer_read = 0; 1084 | int total_read = 0; 1085 | int message_len = -1; 1086 | 1087 | //buffered read from socket 1088 | int i = 0; 1089 | while(1) { 1090 | buffer_read = recv(sock, buffer, MAX_BUFFER_SIZE, 0); 1091 | if (buffer_read > 0) { 1092 | client_message = realloc(client_message, total_read + buffer_read + 1); 1093 | if (client_message == NULL) { 1094 | perror("Error ! Memory not allocated client_message"); 1095 | exit(1); 1096 | } 1097 | buffer[buffer_read] = '\0'; 1098 | memcpy(client_message + total_read, buffer, buffer_read); 1099 | total_read = total_read + buffer_read; 1100 | i++; 1101 | if (-1 == message_len) { 1102 | message_len = detectMessageLength(buffer, total_read); 1103 | } 1104 | } else if (buffer_read < 0) { 1105 | perror("[ERROR]: reading from socket"); 1106 | printf("WSAGetLastError() %i\n", WSAGetLastError()); //win 1107 | exit(1); 1108 | } 1109 | if (buffer_read == 0 || total_read >= message_len || -2 == message_len) { 1110 | /* message received successfully */ 1111 | break; 1112 | } 1113 | } 1114 | if (buffer == NULL) { 1115 | perror("Error ! Try to free not allocated memory buffer"); 1116 | exit(1); 1117 | } 1118 | free(buffer); 1119 | client_message[total_read] = '\0'; 1120 | printf("[DEBUG]: Post request length: %lu\n", (unsigned long) strlen(client_message)); 1121 | 1122 | char *decodedJSON; 1123 | char *encodedJSONPostData = strstr(client_message, CK_JSON_KEY); 1124 | if (encodedJSONPostData != NULL) { 1125 | char *encodedJSON = encodedJSONPostData + strlen(CK_JSON_KEY); 1126 | decodedJSON = url_decode(encodedJSON, total_read - (encodedJSON - client_message)); 1127 | free(client_message); 1128 | } else { 1129 | decodedJSON = client_message; 1130 | } 1131 | 1132 | cJSON *commandJSON = cJSON_Parse(decodedJSON); 1133 | free(decodedJSON); 1134 | if (!commandJSON) { 1135 | sendErrorMessage(sock, "Invalid action JSON format for message", ERROR_CODE); 1136 | return; 1137 | } 1138 | 1139 | 1140 | cJSON *secretkeyJSON = cJSON_GetObjectItem(commandJSON, JSON_PARAM_NAME_SECRETKEY); 1141 | if (!secretkeyJSON) { 1142 | if (commandJSON != NULL) { 1143 | cJSON_Delete(commandJSON); 1144 | } 1145 | sendErrorMessage(sock, ERROR_MESSAGE_SECRET_KEY_MISSMATCH, ERROR_CODE_SECRET_KEY_MISMATCH); 1146 | return; 1147 | } 1148 | char *clientSecretKey = secretkeyJSON->valuestring; 1149 | printf("[DEBUG]: Got secretkey: %s from client\n", clientSecretKey); 1150 | if (!serverSecretKey || strncmp(clientSecretKey, serverSecretKey, strlen(serverSecretKey)) == 0 ) { 1151 | cJSON *actionJSON = cJSON_GetObjectItem(commandJSON, JSON_PARAM_NAME_COMMAND); 1152 | if (!actionJSON) { 1153 | printf("[ERROR]: Invalid action JSON format for message: \n"); 1154 | if (commandJSON != NULL) { 1155 | cJSON_Delete(commandJSON); 1156 | } 1157 | sendErrorMessage(sock, "Invalid action JSON format for message: no action found", ERROR_CODE); 1158 | return; 1159 | } 1160 | char *action = actionJSON->valuestring; 1161 | 1162 | printf("[INFO]: Get action: %s\n", action); 1163 | char *resultJSONtext; 1164 | if (strncmp(action, JSCON_PARAM_VALUE_PUSH, 4) == 0) { 1165 | processPush(sock, baseDir, commandJSON); 1166 | } else if (strncmp(action, "pull", 4) == 0) { 1167 | processPull(sock, baseDir, commandJSON); 1168 | } else if (strncmp(action, "shell", 4) == 0) { 1169 | processShell(sock, commandJSON, baseDir); 1170 | } else if (strncmp(action, "state", 4) == 0) { 1171 | processState(sock, baseDir); 1172 | } else if (strncmp(action, "shutdown", 4) == 0) { 1173 | printf("[DEBUG]: Start shutdown CK node"); 1174 | sendHttpResponse(sock, 200, "", 0); 1175 | } else { 1176 | sendErrorMessage(sock, "unknown action", ERROR_CODE); 1177 | } 1178 | } else { 1179 | sendErrorMessage(sock, ERROR_MESSAGE_SECRET_KEY_MISSMATCH, ERROR_CODE_SECRET_KEY_MISMATCH); 1180 | } 1181 | cJSON_Delete(commandJSON); 1182 | 1183 | printf("[INFO]: Action completed successfuly\n"); 1184 | } 1185 | 1186 | -------------------------------------------------------------------------------- /src/net_uuid.c: -------------------------------------------------------------------------------- 1 | /* what follows is a somewhat stripped-down version of the sample 2 | implementation of UUID generation from RFC 4122. */ 3 | 4 | /* 5 | ** Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. 6 | ** Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & 7 | ** Digital Equipment Corporation, Maynard, Mass. 8 | ** Copyright (c) 1998 Microsoft. 9 | ** To anyone who acknowledges that this file is provided "AS IS" 10 | ** without any express or implied warranty: permission to use, copy, 11 | ** modify, and distribute this file for any purpose is hereby 12 | ** granted without fee, provided that the above copyright notices and 13 | ** this notice appears in all source code copies, and that none of 14 | ** the names of Open Software Foundation, Inc., Hewlett-Packard 15 | ** Company, Microsoft, or Digital Equipment Corporation be used in 16 | ** advertising or publicity pertaining to distribution of the software 17 | ** without specific, written prior permission. Neither Open Software 18 | ** Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital 19 | ** Equipment Corporation makes any representations about the 20 | ** suitability of this software for any purpose. 21 | */ 22 | 23 | #ifdef HAVE_CONFIG_H 24 | #include "config.h" 25 | #endif 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #if defined(HAVE_UNISTD_H) 34 | #include 35 | #endif 36 | 37 | #if defined(HAVE_INTTYPES_H) 38 | #include 39 | #endif 40 | 41 | /* set the following to the number of 100ns ticks of the actual 42 | resolution of your system's clock */ 43 | #define UUIDS_PER_TICK 1024 44 | 45 | #ifdef _WIN32 46 | #define WIN32_LEAN_AND_MEAN 1 47 | #include 48 | #include "stdint.h" 49 | #include 50 | #define snprintf _snprintf 51 | #else 52 | 53 | #if HAVE_SYS_TYPES_H 54 | #include 55 | #else 56 | # if HAVE_STDINT_H 57 | # include 58 | # endif 59 | #endif 60 | 61 | #ifdef __MINGW32__ 62 | # include 63 | #else 64 | # if HAVE_SYS_TIME_H 65 | # include 66 | # endif 67 | #endif 68 | 69 | #if HAVE_SYS_SYSINFO_H 70 | #include 71 | #endif 72 | 73 | #endif 74 | 75 | /* system dependent call to get the current system time. Returned as 76 | 100ns ticks since UUID epoch, but resolution may be less than 77 | 100ns. */ 78 | 79 | #ifdef _WIN32 80 | #define I64(C) C 81 | #else 82 | #define I64(C) C##LL 83 | #endif 84 | 85 | #include "net_uuid.h" 86 | 87 | 88 | /* some forward declarations. kind of wimpy to do that but heck, we 89 | are all friends here right? raj 20081024 */ 90 | static uint16_t true_random(void); 91 | 92 | 93 | 94 | #ifdef _WIN32 95 | 96 | static void get_system_time(uuid_time_t *uuid_time) 97 | { 98 | ULARGE_INTEGER time; 99 | 100 | /* NT keeps time in FILETIME format which is 100ns ticks since 101 | Jan 1, 1601. UUIDs use time in 100ns ticks since Oct 15, 1582. 102 | The difference is 17 Days in Oct + 30 (Nov) + 31 (Dec) 103 | + 18 years and 5 leap days. */ 104 | GetSystemTimeAsFileTime((FILETIME *)&time); 105 | time.QuadPart += 106 | 107 | (unsigned __int64) (1000*1000*10) // seconds 108 | * (unsigned __int64) (60 * 60 * 24) // days 109 | * (unsigned __int64) (17+30+31+365*18+5); // # of days 110 | *uuid_time = time.QuadPart; 111 | } 112 | 113 | /* Sample code, not for use in production; see RFC 1750 */ 114 | static void get_random_info(char seed[16]) 115 | { 116 | uint16_t myrand; 117 | int i; 118 | 119 | i = 0; 120 | do { 121 | myrand = true_random(); 122 | seed[i++] = myrand & 0xff; 123 | seed[i++] = myrand >> 8; 124 | } while (i < 14); 125 | 126 | } 127 | 128 | #else 129 | 130 | static void get_system_time(uuid_time_t *uuid_time) 131 | { 132 | struct timeval tp; 133 | 134 | gettimeofday(&tp, (struct timezone *)0); 135 | 136 | /* Offset between UUID formatted times and Unix formatted times. 137 | UUID UTC base time is October 15, 1582. 138 | Unix base time is January 1, 1970.*/ 139 | *uuid_time = ((uint64_t)tp.tv_sec * 10000000) 140 | + ((uint64_t)tp.tv_usec * 10) 141 | + I64(0x01B21DD213814000); 142 | } 143 | 144 | /* Sample code, not for use in production; see RFC 1750 */ 145 | static void get_random_info(char seed[16]) 146 | { 147 | int fd; 148 | uint16_t myrand; 149 | int i; 150 | 151 | /* we aren't all that picky, and we would rather not block so we 152 | will use urandom */ 153 | fd = open("/dev/urandom", O_RDONLY); 154 | 155 | if (fd != -1) { 156 | read(fd, seed, 16); 157 | close(fd); 158 | return; 159 | } 160 | 161 | /* ok, now what? */ 162 | 163 | i = 0; 164 | do { 165 | myrand = true_random(); 166 | seed[i++] = myrand & 0xff; 167 | seed[i++] = myrand >> 8; 168 | } while (i < 14); 169 | 170 | } 171 | 172 | #endif 173 | 174 | 175 | /* true_random -- generate a crypto-quality random number. 176 | **This sample doesn't do that.** */ 177 | static uint16_t true_random(void) 178 | { 179 | static int inited = 0; 180 | uuid_time_t time_now; 181 | 182 | if (!inited) { 183 | get_system_time(&time_now); 184 | time_now = time_now / UUIDS_PER_TICK; 185 | srand((unsigned int) 186 | (((time_now >> 32) ^ time_now) & 0xffffffff)); 187 | inited = 1; 188 | } 189 | 190 | return (uint16_t)rand(); 191 | } 192 | 193 | /* puid -- print a UUID */ 194 | void puid(netperf_uuid_t u) 195 | { 196 | int i; 197 | 198 | printf("%8.8x-%4.4x-%4.4x-%2.2x%2.2x-", u.time_low, u.time_mid, 199 | u.time_hi_and_version, u.clock_seq_hi_and_reserved, 200 | u.clock_seq_low); 201 | for (i = 0; i < 6; i++) 202 | printf("%2.2x", u.node[i]); 203 | printf("\n"); 204 | } 205 | 206 | /* snpuid -- print a UUID in the supplied buffer */ 207 | void snpuid(char *str, size_t size, netperf_uuid_t u) { 208 | int i; 209 | char *tmp = str; 210 | 211 | if (size < DEFAULT_UUID_SIZE) { 212 | snprintf(tmp,size,"%s","uuid string too small"); 213 | return; 214 | } 215 | 216 | /* perhaps this is a trifle optimistic but what the heck */ 217 | sprintf(tmp, 218 | "%8.8x-%4.4x-%4.4x-%2.2x%2.2x-", 219 | u.time_low, 220 | u.time_mid, 221 | u.time_hi_and_version, 222 | u.clock_seq_hi_and_reserved, 223 | u.clock_seq_low); 224 | tmp += 24; 225 | for (i = 0; i < 6; i++) { 226 | sprintf(tmp,"%2.2x", u.node[i]); 227 | tmp += 2; 228 | } 229 | *tmp = 0; 230 | 231 | } 232 | 233 | /* get-current_time -- get time as 60-bit 100ns ticks since UUID epoch. 234 | Compensate for the fact that real clock resolution is 235 | less than 100ns. */ 236 | static void get_current_time(uuid_time_t *timestamp) 237 | { 238 | static int inited = 0; 239 | static uuid_time_t time_last; 240 | static uint16_t uuids_this_tick; 241 | uuid_time_t time_now; 242 | 243 | if (!inited) { 244 | get_system_time(&time_now); 245 | uuids_this_tick = UUIDS_PER_TICK; 246 | inited = 1; 247 | } 248 | 249 | for ( ; ; ) { 250 | get_system_time(&time_now); 251 | 252 | /* if clock reading changed since last UUID generated, */ 253 | if (time_last != time_now) { 254 | /* reset count of uuids gen'd with this clock reading */ 255 | uuids_this_tick = 0; 256 | time_last = time_now; 257 | break; 258 | } 259 | if (uuids_this_tick < UUIDS_PER_TICK) { 260 | uuids_this_tick++; 261 | break; 262 | } 263 | /* going too fast for our clock; spin */ 264 | } 265 | /* add the count of uuids to low order bits of the clock reading */ 266 | *timestamp = time_now + uuids_this_tick; 267 | } 268 | 269 | 270 | /* system dependent call to get IEEE node ID. 271 | This sample implementation generates a random node ID. */ 272 | /* netperf mod - don't bother trying to read or write the nodeid */ 273 | static void get_ieee_node_identifier(uuid_node_t *node) 274 | { 275 | static int inited = 0; 276 | static uuid_node_t saved_node; 277 | char seed[16]; 278 | 279 | if (!inited) { 280 | get_random_info(seed); 281 | seed[0] |= 0x01; 282 | memcpy(&saved_node, seed, sizeof saved_node); 283 | } 284 | inited = 1; 285 | 286 | *node = saved_node; 287 | } 288 | 289 | 290 | /* format_uuid_v1 -- make a UUID from the timestamp, clockseq, 291 | and node ID */ 292 | static void format_uuid_v1(netperf_uuid_t* uuid, uint16_t clock_seq, 293 | uuid_time_t timestamp, uuid_node_t node) 294 | { 295 | /* Construct a version 1 uuid with the information we've gathered 296 | plus a few constants. */ 297 | uuid->time_low = (unsigned long)(timestamp & 0xFFFFFFFF); 298 | uuid->time_mid = (unsigned short)((timestamp >> 32) & 0xFFFF); 299 | uuid->time_hi_and_version = 300 | (unsigned short)((timestamp >> 48) & 0x0FFF); 301 | uuid->time_hi_and_version |= (1 << 12); 302 | uuid->clock_seq_low = clock_seq & 0xFF; 303 | uuid->clock_seq_hi_and_reserved = (clock_seq & 0x3F00) >> 8; 304 | uuid->clock_seq_hi_and_reserved |= 0x80; 305 | memcpy(&uuid->node, &node, sizeof uuid->node); 306 | } 307 | 308 | /* uuid_create -- generator a UUID */ 309 | int uuid_create(netperf_uuid_t *uuid) 310 | { 311 | uuid_time_t timestamp; 312 | uint16_t clockseq; 313 | uuid_node_t node; 314 | 315 | /* get time, node ID, saved state from non-volatile storage */ 316 | get_current_time(×tamp); 317 | get_ieee_node_identifier(&node); 318 | 319 | /* for us clockseq is always to be random as we have no state */ 320 | clockseq = true_random(); 321 | 322 | /* stuff fields into the UUID */ 323 | format_uuid_v1(uuid, clockseq, timestamp, node); 324 | return 1; 325 | } 326 | 327 | void get_uuid_string(char *uuid_str, size_t size) { 328 | netperf_uuid_t u; 329 | 330 | uuid_create(&u); 331 | snpuid(uuid_str,size,u); 332 | 333 | return; 334 | } 335 | 336 | #ifdef NETPERF_STANDALONE_DEBUG 337 | 338 | 339 | int 340 | main(int argc, char *argv[]) 341 | { 342 | netperf_uuid_t u; 343 | char uuid_str[DEFAULT_UUID_SIZE]; 344 | #if 0 345 | uuid_create(&u); 346 | printf("uuid_create(): "); puid(u); 347 | snpuid(uuid_str,sizeof(uuid_str),u); 348 | printf("\nas a string %s\n",uuid_str); 349 | #endif 350 | get_uuid_string(uuid_str,sizeof(uuid_str)); 351 | printf("uuid_str is %s\n",uuid_str); 352 | return 0; 353 | } 354 | 355 | 356 | #endif -------------------------------------------------------------------------------- /src/net_uuid.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #define DEFAULT_UUID_SIZE 38 5 | 6 | 7 | typedef uint64_t uuid_time_t; 8 | 9 | typedef struct { 10 | char nodeID[6]; 11 | } uuid_node_t; 12 | 13 | typedef struct { 14 | uint32_t time_low; 15 | uint16_t time_mid; 16 | uint16_t time_hi_and_version; 17 | uint8_t clock_seq_hi_and_reserved; 18 | uint8_t clock_seq_low; 19 | uint8_t node[6]; 20 | } netperf_uuid_t; 21 | 22 | /** 23 | * Generates UUID RFC 4122 24 | * Example: 25 | * char compileUUID[38]; 26 | * get_uuid_string(compileUUID,sizeof(compileUUID)); 27 | * 28 | */ 29 | void get_uuid_string(char *uuid_str, size_t size); 30 | -------------------------------------------------------------------------------- /src/urldecoder.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "urldecoder.h" 5 | 6 | 7 | /* Converts a hex character to its integer value */ 8 | char from_hex(char ch) { 9 | return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; 10 | } 11 | 12 | /* Converts an integer value to its hex character*/ 13 | char to_hex(char code) { 14 | static char hex[] = "0123456789abcdef"; 15 | return hex[code & 15]; 16 | } 17 | 18 | /* Returns a url-encoded version of str */ 19 | /* IMPORTANT: be sure to free() the returned string after use */ 20 | char *url_encode(char *str) { 21 | char *pstr = str, *buf = malloc(strlen(str) * 3 + 1), *pbuf = buf; 22 | while (*pstr) { 23 | if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~') 24 | *pbuf++ = *pstr; 25 | else if (*pstr == ' ') 26 | *pbuf++ = '+'; 27 | else 28 | *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15); 29 | pstr++; 30 | } 31 | *pbuf = '\0'; 32 | return buf; 33 | } 34 | 35 | /* Returns a url-decoded version of str */ 36 | /* IMPORTANT: be sure to free() the returned string after use */ 37 | char *url_decode(char *str, size_t size) { 38 | // size_t size = strlen(str) + 1; 39 | char *pstr = str; 40 | char *buf = malloc(size); 41 | char *pbuf = buf; 42 | while (*pstr) { 43 | if (*pstr == '%') { 44 | if (pstr[1] && pstr[2]) { 45 | *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]); 46 | pstr += 2; 47 | } 48 | } else if (*pstr == '+') { 49 | *pbuf++ = ' '; 50 | } else { 51 | *pbuf++ = *pstr; 52 | } 53 | pstr++; 54 | } 55 | *pbuf = '\0'; 56 | return buf; 57 | } -------------------------------------------------------------------------------- /src/urldecoder.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* Returns a url-decoded version of str */ 4 | /* IMPORTANT: be sure to free() the returned string after use */ 5 | char *url_decode(char *str, size_t size); 6 | 7 | /* Returns a url-encoded version of str */ 8 | /* IMPORTANT: be sure to free() the returned string after use */ 9 | char *url_encode(char *str); -------------------------------------------------------------------------------- /tests/ck-master.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctuning/ck-crowdnode/e4251eaea2db1d242d843a5dd12a395829b655e2/tests/ck-master.zip -------------------------------------------------------------------------------- /tests/non-latin.txt: -------------------------------------------------------------------------------- 1 | non-latin русский 2 | -------------------------------------------------------------------------------- /tests/non-latin.txt.win: -------------------------------------------------------------------------------- 1 | non-latin ?? ?????? -------------------------------------------------------------------------------- /tests/test_push_pull.py: -------------------------------------------------------------------------------- 1 | 2 | import shutil 3 | import os 4 | import filecmp 5 | import unittest 6 | 7 | # The following variables are initialized by test runner 8 | ck=None # CK kernel 9 | cfg=None # test config 10 | access_test_repo=None # convenience function to call the test repo without the need to specify its UOA and secretkey. 11 | # You just need to provide 'action' and the action's arguments 12 | 13 | class TestPushPull(unittest.TestCase): 14 | 15 | def test_push_pull(self): 16 | tmp_file = 'ck-push-test.zip' 17 | orig_file = 'ck-master.zip' 18 | shutil.copyfile(orig_file, tmp_file) 19 | try: 20 | access_test_repo({'action': 'push', 'filename': tmp_file}) 21 | 22 | os.remove(tmp_file) 23 | 24 | access_test_repo({'action': 'pull', 'filename': tmp_file}) 25 | 26 | # the downloaded file must match the original file 27 | self.assertTrue(filecmp.cmp(orig_file, tmp_file)) 28 | finally: 29 | try: 30 | os.remove(tmp_file) 31 | except: pass 32 | 33 | def test_extra_path(self): 34 | tmp_file = 'ck-push-test.zip' 35 | orig_file = 'ck-master.zip' 36 | shutil.copyfile(orig_file, tmp_file) 37 | try: 38 | access_test_repo({'action': 'push', 'filename': tmp_file, 'extra_path': 'a'}) 39 | 40 | os.remove(tmp_file) 41 | 42 | access_test_repo({'action': 'pull', 'filename': tmp_file, 'extra_path': 'a'}) 43 | 44 | # the downloaded file must match the original file 45 | self.assertTrue(filecmp.cmp(orig_file, tmp_file)) 46 | finally: 47 | try: 48 | os.remove(tmp_file) 49 | except: pass 50 | 51 | def test_extra_path_2(self): 52 | extra_path1 = '1\\2\\3' if 'Windows' == cfg['platform'] else '1/2/3' 53 | extra_path2 = '11\\22\\33\\44' if 'Windows' == cfg['platform'] else '11/22/33/44' 54 | 55 | orig_file = 'ck-master.zip' 56 | r = access_test_repo({'action': 'push', 'filename': orig_file, 'extra_path': extra_path1}, checkFail=False) 57 | self.assertEqual(0, r['return']) 58 | 59 | # check two times to make sure the server is OK 60 | r = access_test_repo({'action': 'push', 'filename': orig_file, 'extra_path': extra_path2}, checkFail=False) 61 | self.assertEqual(0, r['return']) 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /tests/test_shell.py: -------------------------------------------------------------------------------- 1 | 2 | import filecmp 3 | import unittest 4 | import base64 5 | 6 | # The following variables are initialized by test runner 7 | ck=None # CK kernel 8 | cfg=None # test config 9 | access_test_repo=None # convenience function to call the test repo without the need to specify its UOA and secretkey. 10 | # You just need to provide 'action' and the action's arguments 11 | 12 | class TestPushPull(unittest.TestCase): 13 | 14 | def test_shell(self): 15 | cmd = 'echo test shell stdout' 16 | #different base64 because of OS specific line end 17 | stdoutBase64 = 'dGVzdCBzaGVsbCBzdGRvdXQgCg==' if 'Windows' == cfg['platform'] else 'dGVzdCBzaGVsbCBzdGRvdXQK' 18 | r = access_test_repo({'action': 'shell', 'cmd': cmd}) 19 | self.assertIn('stdout_base64', r) 20 | self.assertEqual(stdoutBase64, r['stdout_base64']) 21 | self.assertIn('return_code', r) 22 | self.assertEqual(0, r['return_code']) 23 | self.assertIn('stderr_base64', r) 24 | self.assertEqual('', r['stderr_base64']) 25 | 26 | 27 | def test_shell_err(self): 28 | cmd = 'nodir C:\\' if 'Windows' == cfg['platform'] else 'nols -l /etc/' 29 | r = access_test_repo({'action': 'shell', 'cmd': cmd}) 30 | self.assertIn('stdout_base64', r) 31 | self.assertEqual('', r['stdout_base64']) 32 | self.assertIn('return_code', r) 33 | self.assertNotEqual(0, r['return_code']) 34 | self.assertIn('stderr_base64', r) 35 | self.assertNotEqual('', r['stderr_base64']) 36 | 37 | 38 | def test_non_latin(self): 39 | fname = 'non-latin.txt' 40 | access_test_repo({'action': 'push', 'filename': fname}) 41 | cmd = 'type ' + fname if 'Windows' == cfg['platform'] else 'cat ' + fname 42 | 43 | r = ck.convert_file_to_upload_string({'filename': fname}) 44 | base64_content = r['file_content_base64'] 45 | 46 | r = access_test_repo({'action': 'shell', 'cmd': cmd}) 47 | self.assertEqual(base64_content, r['stdout_base64']) 48 | 49 | r = ck.convert_upload_string_to_file({'file_content_base64': r['stdout_base64']}) 50 | try: 51 | self.assertTrue(filecmp.cmp(fname, r['filename'])) 52 | finally: 53 | try: 54 | os.remove(tmp_file) 55 | except: pass 56 | 57 | def test_non_latin_encoding(self): 58 | fname = 'non-latin.txt.win' if 'Windows' == cfg['platform'] else 'non-latin.txt' 59 | fenc = '1252' if 'Windows' == cfg['platform'] else 'utf8' 60 | 61 | rl = ck.load_text_file({'text_file':fname, 'encoding':fenc}) 62 | fcontent = rl['string'] 63 | access_test_repo({'action': 'push', 'filename': fname}) 64 | cmd = 'type ' + fname if 'Windows' == cfg['platform'] else 'cat ' + fname 65 | r = access_test_repo({'action': 'shell', 'cmd': cmd}) 66 | 67 | xso=str(r.get('stdout_base64','')) 68 | enc=r.get('encoding','') 69 | so='' 70 | if xso!='': 71 | so=base64.urlsafe_b64decode(xso, ) 72 | if type(so)==bytes: 73 | so=so.decode(encoding=enc, errors='ignore') 74 | try: 75 | self.assertEquals(fcontent, so) 76 | finally: 77 | try: 78 | os.remove(tmp_file) 79 | except: pass 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /tests/test_state.py: -------------------------------------------------------------------------------- 1 | 2 | import unittest 3 | 4 | # The following variables are initialized by test runner 5 | ck=None # CK kernel 6 | cfg=None # test config 7 | access_test_repo=None # convenience function to call the test repo without the need to specify its UOA and secretkey. 8 | # You just need to provide 'action' and the action's arguments 9 | files_dir = None # Path to files from config 10 | 11 | class TestPushPull(unittest.TestCase): 12 | 13 | def test_state(self): 14 | r = access_test_repo({'action': 'state'}) 15 | cfg_ = r['cfg'] 16 | self.assertIn('path_to_files', cfg_) 17 | self.assertEqual(files_dir, cfg_['path_to_files']) 18 | self.assertIn('return', r) 19 | self.assertEqual(0, r['return']) 20 | 21 | --------------------------------------------------------------------------------