summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorChris Engel <cjengel@us.ibm.com>2017-08-22 14:09:18 -0500
committerChris Engel <cjengel@us.ibm.com>2017-08-28 16:17:16 -0500
commit30409d59f60314da8e060ae4af18774289073752 (patch)
tree7d57265817454e197eb488aaa029e2691f1ca414 /src
parent200cf3ccb19e801707cc824f62d143e80dff1b09 (diff)
downloadsb-signing-framework-30409d59f60314da8e060ae4af18774289073752.tar.gz
sb-signing-framework-30409d59f60314da8e060ae4af18774289073752.zip
Initial drop of signing framework client
Diffstat (limited to 'src')
-rw-r--r--src/client/client.c663
-rw-r--r--src/client/makefile40
-rw-r--r--src/client/pscp_json.c448
-rw-r--r--src/client/pscp_json.h26
-rw-r--r--src/client/pscp_sftp.c462
-rw-r--r--src/client/pscp_sftp.h32
-rw-r--r--src/client/pscp_sftp_test.c67
7 files changed, 1738 insertions, 0 deletions
diff --git a/src/client/client.c b/src/client/client.c
new file mode 100644
index 0000000..06cd91b
--- /dev/null
+++ b/src/client/client.c
@@ -0,0 +1,663 @@
+/* Copyright 2017 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <fcntl.h>
+
+#include "pscp_sftp.h"
+#include "pscp_json.h"
+
+bool GetArgs(int argc, char **argv);
+void PrintUsage(void);
+bool PrivateKeyEncrypted(const char* privKeyPath);
+int GenerateFilename(char** filename, const char* directory, const char* base, const char* tag, bool verbose);
+
+const char* pscp_request_tag = ".request";
+const char* pscp_request_go_tag = ".request.go";
+const char* pscp_response_tag = ".response";
+const char* pscp_response_go_tag = ".response.go";
+const char* pscp_remote_directory = "dropbox/";
+const char* pscp_tmp_directory = "/tmp/";
+
+
+// Required Parameters
+char* pscp_project = NULL;
+char* pscp_epwd_path = NULL;
+char* pscp_sftp_url = NULL;
+char* pscp_pkey_path = NULL;
+char* pscp_comment = NULL;
+// Optional Parameters
+char* pscp_parameters = NULL;
+char* pscp_payload_path = NULL;
+char* pscp_output_file = NULL;
+bool verbose = false;
+bool debug = false;
+bool pscp_print_server_stdout = false;
+
+int main(int argc, char** argv)
+{
+ char* pscp_identifier = NULL;
+ char* json_string = NULL;
+ char* pscp_session_id = NULL;
+ char* pscp_request_filename = NULL;
+ char* pscp_request_go_filename = NULL;
+ char* pscp_response_filename = NULL;
+ char* pscp_response_go_filename = NULL;
+
+ struct pscp_sftp_session* pscp_sftp_session = NULL;
+
+ int status = 0; // Indicates what type of failure occured
+ bool success = GetArgs(argc, argv); // Used to check failure in code
+
+ if(!success)
+ {
+ // This silences the generic "ERROR" message that occurs if status is not set to something specific by GetArgs()
+ status = -1;
+ }
+
+ // Verify that required parameters are found
+ if(success && !(pscp_project && pscp_epwd_path && pscp_sftp_url && pscp_pkey_path))
+ {
+ success = false;
+ fprintf(stderr, "ERROR: not all required parameters were found\n");
+ PrintUsage();
+ }
+
+ // Check that the private key is encrypted
+ if(success)
+ {
+ if(!PrivateKeyEncrypted(pscp_pkey_path))
+ {
+ success = false;
+ fprintf(stderr, "ERROR: RSA private key must be encrypted\n");
+ }
+ }
+
+ // Create the identifier for the user: <username>_<hostname>
+ if(success)
+ {
+ const char * username = getenv("USER");
+ const char * hostname = getenv("HOSTNAME");
+ const char * nullString = "NULL"; // Used if no username or hostname is found
+
+ size_t identifierMaxSize = strlen(username)
+ + strlen(hostname)
+ + 2; // Seperator and \0
+
+ pscp_identifier = (char*)calloc(identifierMaxSize, 1);
+
+ if(pscp_identifier)
+ {
+ if(username && hostname)
+ {
+ snprintf(pscp_identifier, identifierMaxSize, "%s_%s", username, hostname);
+ }
+ else if(username)
+ {
+ fprintf(stderr, "ERROR: Unable to find hostname in the environment\n");
+ strncpy(pscp_identifier, username, identifierMaxSize);
+ }
+ else if(hostname)
+ {
+ fprintf(stderr, "ERROR: Unable to find username in the environment\n");
+ strncpy(pscp_identifier, hostname, identifierMaxSize);
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: Unable to find username or hostname in the environment\n");
+ strncpy(pscp_identifier, nullString, identifierMaxSize);
+ }
+
+ if(strlen(pscp_identifier) == 0)
+ {
+ fprintf(stderr, "ERROR: Failure in generation of pscp_identifier\n");
+ success = false;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: Unable to allocate memory for pscp_identifier\n");
+ success = false;
+ }
+ }
+
+ // Create the session id: <username>_<hostname>_<timestamp>. This is also used as the base for filenames.
+ if(success)
+ {
+ time_t timestamp = time(NULL);
+ size_t len = strlen(pscp_identifier)
+ + (((int)log10(timestamp))+1)
+ + 2;
+ pscp_session_id = (char*)calloc(len, 1);
+ if(pscp_session_id)
+ {
+ int rc = snprintf(pscp_session_id, len, "%s_%lx", pscp_identifier, timestamp);
+ if(rc <= 0)
+ {
+ success = false;
+ fprintf(stderr, "ERROR: snprintf for generating pscp_session_id failed\n");
+ }
+ }
+ else
+ {
+ success = false;
+ fprintf(stderr, "ERROR: unable to allocate memory for pscp_session_id\n");
+ }
+ }
+
+ // Generate the filenames for communication with the server
+ if(success)
+ {
+ status = GenerateFilename(&pscp_request_filename, pscp_tmp_directory, pscp_session_id, pscp_request_tag, verbose);
+ if(status != 0)
+ {
+ fprintf(stderr, "ERROR: could not generate name for server-side %s file\n", pscp_request_tag);
+ success = false;
+ }
+ }
+ if(success)
+ {
+ status = GenerateFilename(&pscp_request_go_filename, pscp_tmp_directory, pscp_session_id, pscp_request_go_tag, verbose);
+ if(status != 0)
+ {
+ fprintf(stderr, "ERROR: could not generate name for server-side %s file\n", pscp_request_go_tag);
+ success = false;
+ }
+ }
+ if(success)
+ {
+ status = GenerateFilename(&pscp_response_filename, pscp_tmp_directory, pscp_session_id, pscp_response_tag, verbose);
+ if(status != 0)
+ {
+ success = false;
+ fprintf(stderr, "ERROR: could not generate name for server-side %s file\n", pscp_response_tag);
+ }
+ }
+ if(success)
+ {
+ status = GenerateFilename(&pscp_response_go_filename, pscp_tmp_directory, pscp_session_id, pscp_response_go_tag, verbose);
+ if(status != 0)
+ {
+ success = false;
+ fprintf(stderr, "ERROR: could not generate name for server-side %s file\n", pscp_response_go_tag);
+ }
+ }
+
+ // Generate the json from the parameters
+ if(success)
+ {
+ int rc = createJsonString(&json_string, pscp_project, pscp_parameters, pscp_identifier, pscp_comment, pscp_epwd_path, pscp_payload_path, verbose);
+ if(rc != 0)
+ {
+ fprintf(stderr, "ERROR: unable to create json: %d\n", rc);
+ success = false;
+ status = rc;
+ }
+ }
+
+ // Write json string to a new request file
+ if(success)
+ {
+ // Restrict access to request file to protect the encrypted password
+ int fd = open(pscp_request_filename, O_CREAT | O_WRONLY, 0600);
+ if(fd >= 0)
+ {
+ FILE* fp = fdopen(fd, "w");
+ if(fp)
+ {
+ size_t json_string_len = strlen(json_string);
+ size_t len_write = fwrite(json_string, 1, json_string_len, fp);
+ if(len_write != json_string_len)
+ {
+ fprintf(stderr, "ERROR: write to file was unsuccesful\n");
+ success = false;
+ }
+ fclose(fp);
+ }
+ else
+ {
+ success = false;
+ fprintf(stderr, "ERROR: unable to convert file descriptor to FILE type: %s\n", pscp_request_filename);
+ }
+ close(fd);
+ }
+ else
+ {
+ success = false;
+ fprintf(stderr, "ERROR: unable to open request file for writing: %s\n", pscp_request_filename);
+ }
+ }
+
+ // Initialize the sftp library
+ if(success)
+ {
+ int rc = pscp_sftp_global_init();
+ if(rc != 0)
+ {
+ fprintf(stderr, "ERROR in sftp library initialization\n");
+ success = false;
+ status = rc;
+ }
+ }
+
+ // Create the sftp session
+ if(success)
+ {
+ pscp_sftp_session = startSftpSession(pscp_sftp_url, pscp_pkey_path, verbose);
+ if(!pscp_sftp_session)
+ {
+ fprintf(stderr, "ERROR: unable to create pscp_curl session\n");
+ success = false;
+ }
+ }
+
+ // Send request file server
+ if(success)
+ {
+ char* remote_request_filename = NULL;
+ status = GenerateFilename(&remote_request_filename, pscp_remote_directory, pscp_session_id, pscp_request_tag, verbose);
+ if(status == 0)
+ {
+ status = sendFileToServer(pscp_sftp_session, pscp_request_filename, remote_request_filename);
+ free(remote_request_filename);
+ if(status != 0)
+ {
+ success = false;
+ fprintf(stderr, "ERROR: curl send failed for file %s\n", pscp_request_filename);
+ }
+ }
+ else
+ {
+ success = false;
+ fprintf(stderr, "ERROR: couldn't allocate memory for remote file name\n");
+ }
+ }
+
+ // Send the GO file to server to start the signing
+ if(success)
+ {
+ FILE * fp = fopen(pscp_request_go_filename, "w");
+ if(fp)
+ {
+ fclose(fp);
+ char* remote_request_go_filename = NULL;
+ status = GenerateFilename(&remote_request_go_filename, pscp_remote_directory, pscp_session_id, pscp_request_go_tag, verbose);
+ if(status == 0)
+ {
+ status = sendFileToServer(pscp_sftp_session, pscp_request_go_filename, remote_request_go_filename);
+ if(status != 0)
+ {
+ success = false;
+ fprintf(stderr, "ERROR: curl send failed for file %s\n", pscp_request_go_filename);
+ }
+ free(remote_request_go_filename);
+ }
+ else
+ {
+ success = false;
+ fprintf(stderr, "ERROR: couldn't allocate memory for remote file name\n");
+ }
+ }
+ else
+ {
+ success = false;
+ fprintf(stderr, "ERROR: unable to open request.go file for writing: %s\n", pscp_request_go_filename);
+ }
+ }
+
+ // Wait for signing server to create the response GO file
+ if(success)
+ {
+ char* remote_response_go_filename = NULL;
+ status = GenerateFilename(&remote_response_go_filename, pscp_remote_directory, pscp_session_id, pscp_response_go_tag, verbose);
+ if(status == 0)
+ {
+ status = pollOnFileFromServer(pscp_sftp_session, pscp_response_go_filename, remote_response_go_filename);
+ if(status != 0)
+ {
+ success = false;
+ fprintf(stderr, "ERROR: no response from server\n");
+ }
+ free(remote_response_go_filename);
+ }
+ else
+ {
+ success = false;
+ fprintf(stderr, "ERROR: could not generate %s filename\n", pscp_response_go_tag);
+ }
+ }
+
+ // Copy the signing server response file to the client machine
+ if(success)
+ {
+ char* remote_response_filename = NULL;
+ status = GenerateFilename(&remote_response_filename, pscp_remote_directory, pscp_session_id, pscp_response_tag, verbose);
+ if(status == 0)
+ {
+ status = getFileFromServer(pscp_sftp_session, pscp_response_filename, remote_response_filename);
+ if(status != 0)
+ {
+ success = false;
+ fprintf(stderr, "ERROR: unable to retreive file from server\n");
+ }
+ free(remote_response_filename);
+ }
+ else
+ {
+ success = false;
+ fprintf(stderr, "ERROR: unable to generate remote filename\n");
+ }
+ }
+
+ // Parse data from the server response and display it appropriately
+ if(success)
+ {
+ int retval = 0;
+ unsigned char* payload = NULL;
+ char* stdout = NULL;
+ size_t payload_len = 0;
+ size_t stdout_len = 0;
+
+ status = parseServerResponse(pscp_response_filename, &payload, &payload_len, &stdout, &stdout_len, &retval, verbose);
+ if(status == 0)
+ {
+ status = retval;
+
+ if(payload)
+ {
+ if(pscp_output_file)
+ {
+ FILE* fp = fopen(pscp_output_file, "w");
+ if(fp)
+ {
+ size_t len = fwrite(payload, 1, payload_len, fp);
+ if(len != payload_len)
+ {
+ success = false;
+ fprintf(stderr, "ERROR: unable to write server payload to file. Only %lu/%lu bytes written\n", len, payload_len);
+ }
+ fclose(fp);
+ }
+ else
+ {
+ success = false;
+ fprintf(stderr, "ERROR: unable to open output file\n");
+ }
+ }
+ else
+ {
+ success = false;
+ fprintf(stderr, "ERROR: no output filename was provided, unable to write reponse payload\n");
+ }
+ free(payload);
+ payload = NULL;
+ }
+ if(stdout)
+ {
+ if(pscp_print_server_stdout)
+ {
+ printf("\n==== Begin Standard Out ====\n%s\n==== End of Standard Out ====\n", stdout);
+ }
+ free(stdout);
+ }
+ if(retval != 0)
+ {
+ fprintf(stderr, "Signing server responded with failure: %d\n"
+ "Rerun with -stdout to see server output\n", retval);
+ }
+ }
+ else
+ {
+ success = false;
+ fprintf(stderr, "ERROR: could not parse server response\n");
+ }
+ }
+
+ // Remove the created files from the local machine
+ if(!debug)
+ {
+ if(verbose) printf("Removing auto-generated files\n");
+ remove(pscp_request_filename);
+ remove(pscp_request_go_filename);
+ remove(pscp_response_filename);
+ remove(pscp_response_go_filename);
+ }
+
+ if(success && debug)
+ {
+ printf("\nServer response written to: %s\n", pscp_response_filename);
+ }
+
+ if(pscp_sftp_session)
+ {
+ closeSftpSession(pscp_sftp_session);
+ pscp_sftp_session = NULL;
+ }
+ if(pscp_identifier)
+ {
+ free(pscp_identifier);
+ pscp_identifier = NULL;
+ }
+ if(json_string)
+ {
+ free(json_string);
+ json_string = NULL;
+ }
+ if(pscp_session_id)
+ {
+ free(pscp_session_id);
+ pscp_session_id = NULL;
+ }
+ if(pscp_request_filename)
+ {
+ free(pscp_request_filename);
+ pscp_request_filename = NULL;
+ }
+ if(pscp_request_go_filename)
+ {
+ free(pscp_request_go_filename);
+ pscp_request_go_filename = NULL;
+ }
+ if(pscp_response_filename)
+ {
+ free(pscp_response_filename);
+ pscp_response_filename = NULL;
+ }
+ if(pscp_response_go_filename)
+ {
+ free(pscp_response_go_filename);
+ pscp_response_go_filename = NULL;
+ }
+
+ if(!success && (status == 0))
+ {
+ // Set generic error status for non-specific error
+ status = -1;
+ fprintf(stderr, "ERROR\n");
+ }
+
+ if(success && (status == 0))
+ {
+ printf("DONE\n");
+ }
+
+ return status;
+}
+
+bool GetArgs(int argc, char **argv)
+{
+ bool success = true;
+ int i;
+
+ /* command line argument defaults */
+ verbose = false;
+
+ /* get the command line arguments */
+ for (i=1 ; (i<argc) && (success) ; i++) {
+ if (strcmp(argv[i],"-v") == 0) {
+ verbose = true;
+ }
+ else if (strcmp(argv[i],"-d") == 0) {
+ debug = true;
+ }
+ else if (strcmp(argv[i],"-project") == 0) {
+ i++;
+ pscp_project = argv[i];
+ }
+ else if (strcmp(argv[i],"-param") == 0) {
+ i++;
+ pscp_parameters = argv[i];
+ }
+ else if (strcmp(argv[i],"-epwd") == 0) {
+ i++;
+ pscp_epwd_path = argv[i];
+ }
+ else if (strcmp(argv[i],"-payload") == 0) {
+ i++;
+ pscp_payload_path = argv[i];
+ }
+ else if (strcmp(argv[i],"-comments") == 0) {
+ i++;
+ pscp_comment = argv[i];
+ }
+ else if (strcmp(argv[i],"-url") == 0) {
+ i++;
+ pscp_sftp_url = argv[i];
+ }
+ else if (strcmp(argv[i],"-pkey") == 0) {
+ i++;
+ pscp_pkey_path = argv[i];
+ }
+ else if (strcmp(argv[i],"-stdout") == 0) {
+ pscp_print_server_stdout = true;
+ }
+ else if (strcmp(argv[i],"-o") == 0) {
+ i++;
+ pscp_output_file = argv[i];
+ }
+ else if (strcmp(argv[i],"-h") == 0) {
+ PrintUsage();
+ success = false;
+ }
+ else {
+ fprintf(stderr, "\nframework: Error, %s is not a valid option\n",argv[i]);
+ PrintUsage();
+ success = false;
+ }
+ }
+ return success;
+}
+
+void PrintUsage()
+{
+ printf("\n");
+ printf("client:\n"
+ "\t-h \t print usage help\n\n"
+ "\tRequired:\n"
+ "\t\t-project '' - Name of the project\n"
+ "\t\t-comments '' - Identifier/Message for audit log\n"
+ "\t\t-epwd 'path' - File path to the hsm encrypted password\n"
+ "\t\t-url '' - sftp url. Example: sftp://user@address\n"
+ "\t\t-pkey 'path' - File path to the *encrypted* private key file\n"
+ "\tOptional:\n"
+ "\t\t-payload <path> - File path to the binary to be signed\n"
+ "\t\t-param '' - Parameters to be passed to the signing framework. Ex '-v' or '-h'\n"
+ "\t\t-o <file> - output file to save the return payload\n"
+ "\t\t-stdout - Displays the stdout from the server\n"
+ "\tDebugging:\n"
+ "\t\t-v - verbose tracing\n"
+ "\t\t-d - debug mode - files will not be deleted\n");
+ printf("\n");
+ return;
+}
+
+// Verifies that the provided private key is encrypted.
+bool PrivateKeyEncrypted(const char* privKeyPath)
+{
+ bool encrypted = false;
+ FILE * key = fopen(privKeyPath, "r");
+ fseek(key, 0, SEEK_END);
+ int len = ftell(key);
+ rewind(key);
+
+ char * privKey = calloc(len+1, 1);
+ if(privKey)
+ {
+ size_t read_len = fread(privKey, 1, len, key);
+
+ if(read_len == len)
+ {
+ char* location = strstr(privKey, "ENCRYPTED");
+ if(location)
+ {
+ encrypted = true;
+ }
+ }
+ free(privKey);
+ privKey = NULL;
+ }
+ return encrypted;
+}
+
+// Merges the directory, base, and tag into a single string.
+int GenerateFilename(char** filename, const char* directory, const char* base, const char* tag, bool verbose)
+{
+ int status = 0;
+ if(filename && directory && base && tag)
+ {
+ if(*filename)
+ {
+ free(*filename);
+ *filename = NULL;
+ }
+ size_t len = strlen(directory) + strlen(base) + strlen(tag) + 1;
+ *filename = (char*)calloc(len, 1);
+
+ if(*filename)
+ {
+ int rc = snprintf(*filename, len, "%s%s%s", directory, base, tag);
+ if(rc <= 0)
+ {
+ free(*filename);
+ *filename = NULL;
+ fprintf(stderr, "ERROR: generation of filename failed:\n\tdirectory: %s\n\tbase: %s\n\ttag: %s\n", directory, base, tag);
+ status = -1;
+ }
+ else
+ {
+ if(verbose) printf("Filename Generated: %s\n", *filename);
+ }
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: unable to allocate memory for filename:\n\tdirectory: %s\n\tbase: %s\n\ttag: %s\n", directory, base, tag);
+ status = -1;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: null value passed in as argument to generateFilename()\n");
+ status = -1;
+ }
+ return status;
+}
+
+
diff --git a/src/client/makefile b/src/client/makefile
new file mode 100644
index 0000000..d4dc827
--- /dev/null
+++ b/src/client/makefile
@@ -0,0 +1,40 @@
+# Copyright 2017 IBM Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+CC=gcc
+CFLAGS+=-std=gnu99 -Wall -pedantic -I.
+CLIB=-ljson-c -lm -lcurl -lssl -lcrypto
+DEPS = pscp_sftp.h pscp_json.h
+CLIENT_OBJ = client.o
+SFTP_T_OBJ = pscp_sftp_test.o
+COMMON_OBJ = pscp_sftp.o pscp_json.o
+CEXEC=sf_client sftp-test
+
+%.o: %.c $(DEPS)
+ $(CC) -c -o $@ $< $(CFLAGS)
+
+sf_client: $(CLIENT_OBJ) $(COMMON_OBJ)
+ gcc -o $@ $^ $(CFLAGS) $(CLIB)
+
+sftp-test: $(SFTP_T_OBJ) $(COMMON_OBJ)
+ gcc -o $@ $^ $(CFLAGS) $(CLIB)
+
+default: sf_client
+
+all: $(CEXEC)
+
+clean:
+ rm -f $(CEXEC) $(SFTP_T_OBJ) $(CLIENT_OBJ) $(DECRYPT_OBJ) $(COMMON_OBJ)
diff --git a/src/client/pscp_json.c b/src/client/pscp_json.c
new file mode 100644
index 0000000..f36d32f
--- /dev/null
+++ b/src/client/pscp_json.c
@@ -0,0 +1,448 @@
+/* Copyright 2017 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include <json-c/json.h>
+
+#include "pscp_json.h"
+
+char NibbleToChar(const unsigned char nibble, const bool verbose);
+char * ByteArrayToHexAscii(const unsigned char * byte, const size_t len, const bool verbose);
+int HexAsciiToByteArray(unsigned char** byte, size_t* byte_len, const char* ascii, const size_t ascii_len, bool verbose);
+int BinaryFileToJsonObject(struct json_object** json, FILE* fp, const bool verbose);
+void TrimWhitespace(char *str);
+
+char NibbleToChar(const unsigned char nibble, const bool verbose)
+{
+ if(nibble > 0xF)
+ {
+ if(verbose) fprintf(stderr, "ERROR: Data passed into nibbleToChar() is larger than a nibble\n");
+ return 0;
+ }
+ else
+ {
+ if(nibble < 10)
+ {
+ return ('0' + nibble);
+ }
+ else
+ {
+ return ('A' + nibble - 10);
+ }
+ }
+}
+
+unsigned char CharToNibble(char c, bool verbose)
+{
+ c = toupper(c);
+ if(c >= '0' && c <= '9')
+ {
+ return (c - '0');
+ }
+ else if(c >= 'A' && c <= 'F')
+ {
+ return (c - 'A' + 10);
+ }
+ else
+ {
+ return 0xFF;
+ }
+}
+
+char * ByteArrayToHexAscii(const unsigned char * byte, const size_t len, const bool verbose)
+{
+ size_t out_len = len * 2;
+ char* out_str = malloc(out_len + 1);
+ bool success = true;
+
+ for(size_t i = 0; (i < len) && success; i++)
+ {
+ unsigned char hi = (byte[i] >> 4) & 0xF;
+ unsigned char lo = byte[i] & 0x0F;
+ char char_hi = NibbleToChar(hi, verbose);
+ char char_lo = NibbleToChar(lo, verbose);
+ if((char_hi != 0) && (char_lo != 0))
+ {
+ out_str[2*i] = char_hi;
+ out_str[(2*i)+1] = char_lo;
+ }
+ else
+ {
+ if(verbose) fprintf(stderr, "Error in parsing binary file at index %x\n", (unsigned int)i);
+ success = false;
+ }
+ }
+ out_str[out_len] = 0;
+
+ if(!success)
+ {
+ free(out_str);
+ out_str = NULL;
+ }
+ return out_str;
+}
+
+int HexAsciiToByteArray(unsigned char** byte, size_t* byte_len, const char* ascii, const size_t ascii_len, bool verbose)
+{
+ bool success = true;
+ int status = 0;
+ if(byte && *byte == NULL && byte_len && ascii)
+ {
+ if(ascii_len % 2 == 0)
+ {
+ *byte_len = ascii_len / 2;
+ *byte = calloc(*byte_len, 1);
+
+ for(size_t i = 0; (i < *byte_len) && success; i++)
+ {
+ unsigned char hi = CharToNibble(ascii[i*2], verbose);
+ unsigned char lo = CharToNibble(ascii[(i*2)+1], verbose);
+ if(hi != 0xFF && lo != 0xFF)
+ {
+ (*byte)[i] = (0xF0 & (hi << 4)) | (0x0F & lo);
+ }
+ else
+ {
+ if(verbose) fprintf(stderr, "Error in parsing binary from ascii at byte %lu\n", i);
+ success = false;
+ }
+ }
+ }
+ else
+ {
+ success = false;
+ }
+ if(!success && *byte)
+ {
+ free(*byte);
+ }
+ }
+ else
+ {
+ success = false;
+ }
+ if(status == 0 && !success)
+ {
+ status = -1;
+ }
+
+ return status;
+}
+
+int BinaryFileToJsonObject(struct json_object** json, FILE* fp, const bool verbose)
+{
+ int status = 0;
+ bool success = true;
+
+ if(!(json && (*json == NULL) && fp))
+ {
+ success = false;
+ }
+
+ if(success)
+ {
+ fseek(fp, 0, SEEK_END);
+ int fileSize = ftell(fp);
+ rewind(fp);
+
+ unsigned char* hashFile = malloc((fileSize+1));
+ if(hashFile)
+ {
+ size_t read_len = fread(hashFile, 1, fileSize, fp);
+ if(read_len == fileSize)
+ {
+ char* hashString = ByteArrayToHexAscii(hashFile, read_len, verbose);
+
+ if(hashString)
+ {
+ json_object* tmp = json_object_new_string(hashString);
+ if(tmp)
+ {
+ *json = tmp;
+ }
+ else
+ {
+ success = false;
+ }
+ free(hashString);
+ }
+ else
+ {
+ success = false;
+ }
+ }
+ else
+ {
+ success = false;
+ if(verbose) fprintf(stderr, "ERROR: difference between byte read and file size. File size: %d, Bytes read: %d\n", (int)fileSize, (int)read_len);
+ }
+ free(hashFile);
+ }
+ else
+ {
+ success = false;
+ if(verbose) fprintf(stderr, "ERROR: Unable to allocate memory in binaryFileToJsonObject\n");
+ }
+ }
+
+ if(!success && status == 0)
+ {
+ status = -1;
+ }
+
+ return status;
+}
+
+int createJsonString(char** rtnStr, const char* pscp_project, const char* pscp_parameters, const char* pscp_user, const char* pscp_comment, const char* pscp_epwd_path, const char* pscp_payload_path, const bool verbose)
+{
+ bool success = true;
+ int status = 0;
+ struct json_object* json = NULL;
+ char* nullStr = "";
+
+ // Check for required arguments
+ if(!(pscp_project && pscp_epwd_path && pscp_user && pscp_comment && rtnStr && (*rtnStr == NULL)))
+ {
+ success = false;
+ }
+
+ if(success)
+ {
+ json = json_object_new_object();
+ if(!json)
+ {
+ success = false;
+ }
+ }
+
+ if(success)
+ {
+ if(!pscp_parameters)
+ {
+ pscp_parameters = nullStr;
+ }
+ }
+
+ // Copy the strings into json objects
+ if(success)
+ {
+ struct json_object* json_project = json_object_new_string(pscp_project);
+ struct json_object* json_parameters = json_object_new_string(pscp_parameters);
+ struct json_object* json_user = json_object_new_string(pscp_user);
+ struct json_object* json_comment = json_object_new_string(pscp_comment);
+
+ if(json_project && json_parameters && json_user && json_comment)
+ {
+ // Ownership of memory is passed to parent json_object
+ json_object_object_add(json, "project", json_project);
+ json_object_object_add(json, "parameters", json_parameters);
+ json_object_object_add(json, "user", json_user);
+ json_object_object_add(json, "comment", json_comment);
+ }
+ else
+ {
+ success = false;
+ if(json_project) json_object_put(json_project);
+ if(json_parameters) json_object_put(json_parameters);
+ if(json_user) json_object_put(json_user);
+ if(json_comment) json_object_put(json_comment);
+ }
+ }
+
+ // Copy epwd file into json
+ if(success)
+ {
+ FILE * epwdFile = fopen(pscp_epwd_path, "r");
+ if(epwdFile)
+ {
+ fseek(epwdFile, 0, SEEK_END);
+ int fileSize = ftell(epwdFile);
+ rewind(epwdFile);
+
+ char* epwd = calloc(fileSize+1, 1);
+ if(epwd)
+ {
+ int len = fread(epwd, 1, fileSize, epwdFile);
+ if(len == fileSize)
+ {
+ TrimWhitespace(epwd);
+ struct json_object* json_epwd = json_object_new_string(epwd);
+ if(json_epwd)
+ {
+ json_object_object_add(json, "epwd", json_epwd);
+ }
+ else
+ {
+ success = false;
+ }
+ }
+ else
+ {
+ if(verbose) fprintf(stderr, "ERROR: Unable to read epwd file %s\n", pscp_epwd_path);
+ success = false;
+ }
+ free(epwd);
+ }
+ else
+ {
+ if(verbose) fprintf(stderr, "ERROR: unable to allocate memory for epwd\n");
+ success = false;
+ }
+ }
+ else
+ {
+ success = false;
+ if(verbose) fprintf(stderr, "ERROR: Unable to open epwd file %s\n", pscp_epwd_path);
+ }
+ }
+ // Read in payload file (optional)
+ if(success && pscp_payload_path)
+ {
+ FILE* fp = fopen(pscp_payload_path, "r");
+ if(fp)
+ {
+ struct json_object* json_payload = NULL;
+ int rc = BinaryFileToJsonObject(&json_payload, fp, verbose);
+ if(rc == 0)
+ {
+ json_object_object_add(json, "payload", json_payload);
+ }
+ else
+ {
+ if(verbose) fprintf(stderr, "ERROR: Unable to stringify binary file %s\n", pscp_payload_path);
+ success = false;
+ status = rc;
+ }
+ fclose(fp);
+ }
+ else
+ {
+ if(verbose) fprintf(stderr, "ERROR: Unable to open payload file %s\n", pscp_payload_path);
+ success = false;
+ }
+ }
+
+ if(success)
+ {
+ const char* json_string = json_object_to_json_string(json);;
+ if(json_string)
+ {
+ // When the json object is free'd, the memory where the string comes from is also lost
+ *rtnStr = strdup(json_string);
+ }
+ else
+ {
+ if(verbose) fprintf(stderr, "ERROR: Unable to generate serialized json string\n");
+ }
+ }
+ if(json)
+ {
+ json_object_put(json);
+ }
+
+ if(!success && status == 0)
+ {
+ status = -1;
+ }
+
+ return status;
+}
+
+int parseServerResponse(const char * responseFile, unsigned char** res_payload, size_t* res_payload_len, char** res_stdout, size_t* res_stdout_len, int* res_retval, bool verbose)
+{
+ int retval = -1;
+ if(responseFile && res_payload && stdout && res_retval && res_payload_len && res_stdout_len)
+ {
+ FILE * fp = fopen(responseFile, "r");
+ if(fp)
+ {
+ fseek(fp, 0, SEEK_END);
+ int fileSize = ftell(fp);
+ rewind(fp);
+
+ *res_payload = NULL;
+ *res_stdout = NULL;
+
+ char * response = malloc(fileSize+1);
+ if(response)
+ {
+ size_t len = fread(response, 1, fileSize, fp);
+ if(len == fileSize)
+ {
+ struct json_object* json = json_tokener_parse(response);
+ if(json)
+ {
+ json_object* tmp = NULL;
+ json_object_object_get_ex(json, "result", &tmp);
+ if(tmp)
+ {
+ const char* json_string = json_object_get_string(tmp);
+ size_t json_string_len = strlen(json_string);
+ HexAsciiToByteArray(res_payload, res_payload_len, json_string, json_string_len, verbose);
+ }
+
+ tmp = NULL;
+ json_object_object_get_ex(json, "stdout", &tmp);
+ if(tmp)
+ {
+ *res_stdout = strdup(json_object_get_string(tmp));
+ *res_stdout_len = strlen(*res_stdout);
+ }
+
+ tmp = NULL;
+ json_object_object_get_ex(json, "retval", &tmp);
+ if(tmp) *res_retval = json_object_get_int(tmp);
+ retval = 0;
+
+ json_object_put(json);
+ }
+ }
+ free(response);
+ }
+ fclose(fp);
+ }
+ }
+ return retval;
+}
+
+// Removes any whitespace in the string
+void TrimWhitespace(char *str)
+{
+ char* p = NULL;
+ char* q = NULL;
+ if(str)
+ {
+ p = str;
+ q = str;
+ while(*q != 0)
+ {
+ if(isspace(*q))
+ {
+ q++;
+ }
+ else
+ {
+ *p = *q;
+ p++;
+ q++;
+ }
+ }
+ *p = 0;
+ }
+}
diff --git a/src/client/pscp_json.h b/src/client/pscp_json.h
new file mode 100644
index 0000000..27802e1
--- /dev/null
+++ b/src/client/pscp_json.h
@@ -0,0 +1,26 @@
+/* Copyright 2017 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PSCP_JSON_H
+#define PSCP_JSON_H
+
+#include <stdbool.h>
+
+int createJsonString(char** rtnStr, const char* pscp_project, const char* pscp_parameters, const char* pscp_user, const char* pscp_comment, const char* pscp_epwd_path, const char* pscp_payload_path, const bool verbose);
+//int encryptJsonString(const char * in, char** out, const size_t in_len, size_t* out_len, bool verbose);
+int parseServerResponse(const char * responseFile, unsigned char** res_payload, size_t* res_payload_len, char** res_stdout, size_t* res_stdout_len, int* res_retval, bool verbose);
+
+#endif
diff --git a/src/client/pscp_sftp.c b/src/client/pscp_sftp.c
new file mode 100644
index 0000000..813950f
--- /dev/null
+++ b/src/client/pscp_sftp.c
@@ -0,0 +1,462 @@
+/* Copyright 2017 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pscp_sftp.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <termios.h>
+#include <curl/curl.h>
+
+#define PSCP_PKEY_PASSPHRASE_MAX 256
+#define PSCP_SFTP_MAX_POLLING_ATTEMPTS 10
+#define PSCP_SFTP_POLLING_DURATION 5
+
+struct pscp_sftp_session
+{
+ CURL* curl;
+ char* url;
+ size_t url_len;
+ bool verbose;
+};
+
+int GetPassword(char* result, size_t len, bool verbose);
+
+int pscp_sftp_global_init()
+{
+ return curl_global_init(CURL_GLOBAL_ALL);
+}
+
+// Common code to initialize sftp connection to remote server.
+struct pscp_sftp_session* startSftpSession(const char * sftp_url, const char * privateKeyPath, bool verbose)
+{
+ int status = 0;
+ struct pscp_sftp_session* sftp = NULL;
+
+ if(!sftp_url || !privateKeyPath)
+ {
+ status = -1;
+ }
+
+ if(status == 0)
+ {
+ sftp = calloc(1, sizeof(struct pscp_sftp_session));
+ if(!sftp)
+ {
+ status = -1;
+ }
+ }
+
+ if(status == 0)
+ {
+ sftp->curl = curl_easy_init();
+ if(!sftp->curl)
+ {
+ status = -1;
+ }
+ }
+
+ if(status == 0 && verbose)
+ {
+ status = curl_easy_setopt(sftp->curl, CURLOPT_VERBOSE, 1L);
+ }
+
+ if(status == 0)
+ {
+ size_t len = strlen(sftp_url);
+ sftp->url_len = len + 2;
+ sftp->url = calloc(sftp->url_len, 1);
+ if(sftp->url)
+ {
+ strncpy(sftp->url, sftp_url, len);
+
+ if(sftp_url[len - 1] != '/')
+ {
+ strncat(sftp->url, "/", sftp->url_len);
+ }
+ }
+ }
+
+ if(status == CURLE_OK) status = curl_easy_setopt(sftp->curl, CURLOPT_PROTOCOLS, CURLPROTO_SFTP);
+ if(status == CURLE_OK) status = curl_easy_setopt(sftp->curl, CURLOPT_SSH_PRIVATE_KEYFILE, privateKeyPath);
+ if(status == CURLE_OK)
+ {
+ char passphrase[PSCP_PKEY_PASSPHRASE_MAX];
+ bzero(passphrase, PSCP_PKEY_PASSPHRASE_MAX);
+ status = GetPassword(passphrase, PSCP_PKEY_PASSPHRASE_MAX, verbose);
+ if(status == 0)
+ {
+ status = curl_easy_setopt(sftp->curl, CURLOPT_KEYPASSWD, passphrase);
+ }
+ bzero(passphrase, PSCP_PKEY_PASSPHRASE_MAX);
+ }
+
+ if(status != 0 && sftp)
+ {
+ if(sftp->curl)
+ {
+ curl_easy_cleanup(sftp->curl);
+ }
+ if(sftp->url)
+ {
+ free(sftp->url);
+ }
+ free(sftp);
+ sftp = NULL;
+ }
+ return sftp;
+}
+
+
+int sendFileToServer(const struct pscp_sftp_session* sftp, const char * local, const char * remote)
+{
+ int status = 0;
+ char* full_url = NULL;
+ FILE* fp = NULL;
+
+ if(!sftp || !local || !remote)
+ {
+ status = -1;
+ }
+ else if(!sftp->curl || !sftp->url)
+ {
+ status = -1;
+ }
+
+ if(status == 0)
+ {
+ size_t len = strlen(remote) + strlen(sftp->url) + 1;
+ full_url = calloc(len, 1);
+ if(full_url)
+ {
+ snprintf(full_url, len, "%s%s", sftp->url, remote);
+ }
+ else
+ {
+ status = -1;
+ }
+ }
+
+ if(status == 0)
+ {
+ fp = fopen(local, "r");
+ if(!fp)
+ {
+ status = -1;
+ }
+ }
+
+ if(status == 0)
+ {
+ status = curl_easy_setopt(sftp->curl, CURLOPT_UPLOAD, 1L);
+ }
+ if(status == 0)
+ {
+ status = curl_easy_setopt(sftp->curl, CURLOPT_READDATA, fp);
+ }
+ if(status == 0)
+ {
+ status = curl_easy_setopt(sftp->curl, CURLOPT_URL, full_url);
+ }
+ if(status == 0)
+ {
+ status = curl_easy_perform(sftp->curl);
+ if(status == CURLE_LOGIN_DENIED)
+ {
+ fprintf(stderr, "%s rejected the provided credentials\n", sftp->url);
+ }
+ }
+
+ if(fp)
+ {
+ fclose(fp);
+ fp = NULL;
+ }
+ if(full_url)
+ {
+ free(full_url);
+ full_url = NULL;
+ }
+
+ return status;
+}
+
+int getFileFromServer(const struct pscp_sftp_session* sftp, const char * local, const char * remote)
+{
+ int status = 0;
+ char* full_url = NULL;
+ FILE* fp = NULL;
+
+ if(!sftp || !local || !remote)
+ {
+ status = -1;
+ }
+ else if(!sftp->curl || !sftp->url)
+ {
+ status = -1;
+ }
+
+ if(status == 0)
+ {
+ size_t len = strlen(remote) + strlen(sftp->url) + 1;
+ full_url = calloc(len, 1);
+ if(full_url)
+ {
+ snprintf(full_url, len, "%s%s", sftp->url, remote);
+ }
+ else
+ {
+ status = -1;
+ }
+ }
+
+ if(status == 0)
+ {
+ fp = fopen(local, "w");
+ if(!fp)
+ {
+ status = -1;
+ }
+ }
+
+ if(status == 0)
+ {
+ status = curl_easy_setopt(sftp->curl, CURLOPT_UPLOAD, 0L);
+ }
+ if(status == 0)
+ {
+ status = curl_easy_setopt(sftp->curl, CURLOPT_WRITEDATA, fp);
+ }
+ if(status == 0)
+ {
+ status = curl_easy_setopt(sftp->curl, CURLOPT_URL, full_url);
+ }
+ if(status == 0)
+ {
+ status = curl_easy_perform(sftp->curl);
+ if(status == CURLE_LOGIN_DENIED)
+ {
+ fprintf(stderr, "%s rejected the provided credentials\n", sftp->url);
+ }
+ }
+
+ if(fp)
+ {
+ fclose(fp);
+ fp = NULL;
+ }
+ if(full_url)
+ {
+ free(full_url);
+ full_url = NULL;
+ }
+
+ return status;
+}
+
+
+int pollOnFileFromServer(const struct pscp_sftp_session* sftp, const char * local, const char * remote)
+{
+ int status = 0;
+ char* full_url = NULL;
+ FILE* fp = NULL;
+
+ if(!sftp || !local || !remote)
+ {
+ status = -1;
+ }
+ else if(!sftp->curl || !sftp->url)
+ {
+ status = -1;
+ }
+
+ if(status == 0)
+ {
+ size_t len = strlen(remote) + strlen(sftp->url) + 1;
+ full_url = calloc(len, 1);
+ if(full_url)
+ {
+ snprintf(full_url, len, "%s%s", sftp->url, remote);
+ }
+ else
+ {
+ status = -1;
+ }
+ }
+
+ if(status == 0)
+ {
+ fp = fopen(local, "w");
+ if(!fp)
+ {
+ status = -1;
+ }
+ }
+
+ if(status == 0)
+ {
+ status = curl_easy_setopt(sftp->curl, CURLOPT_UPLOAD, 0L);
+ }
+ if(status == 0)
+ {
+ status = curl_easy_setopt(sftp->curl, CURLOPT_WRITEDATA, fp);
+ }
+ if(status == 0)
+ {
+ status = curl_easy_setopt(sftp->curl, CURLOPT_URL, full_url);
+ }
+ if(status == 0)
+ {
+ status = curl_easy_perform(sftp->curl);
+ // TODO: have a timeout
+ for(int i = 0; (i < PSCP_SFTP_MAX_POLLING_ATTEMPTS) && (status == CURLE_REMOTE_FILE_NOT_FOUND); i++)
+ {
+ sleep(PSCP_SFTP_POLLING_DURATION);
+ status = curl_easy_perform(sftp->curl);
+ }
+ if(status == CURLE_LOGIN_DENIED)
+ {
+ fprintf(stderr, "%s rejected the provided credentials\n", sftp->url);
+ }
+ }
+
+ if(fp)
+ {
+ fclose(fp);
+ fp = NULL;
+ }
+ if(full_url)
+ {
+ free(full_url);
+ full_url = NULL;
+ }
+
+ return status;
+}
+
+static void SignalHandler(int val)
+{
+ struct termios term;
+ tcgetattr(fileno(stdin), &term);
+
+ term.c_lflag |= ECHO;
+ term.c_lflag &= ~ECHONL;
+
+ tcsetattr(fileno(stdin), TCSAFLUSH, &term);
+ exit(-2);
+}
+
+int GetPassword(char* result, size_t len, bool verbose)
+{
+ int status = 0;
+ bzero(result, len);
+
+ struct sigaction newSa;
+ newSa.sa_handler = SignalHandler;
+ sigemptyset(&newSa.sa_mask);
+ newSa.sa_flags = SA_RESTART;
+
+ struct sigaction oldSigInt;
+ struct sigaction oldSigTerm;
+ struct sigaction oldSigQuit;
+
+ if(sigaction(SIGINT, &newSa, &oldSigInt) == -1)
+ {
+ if(verbose) fprintf(stderr, "ERROR: unable to change SIGINT handler\n");
+ return -1;
+ }
+ if(sigaction(SIGTERM, &newSa, &oldSigTerm) == -1)
+ {
+ if(verbose) fprintf(stderr, "ERROR: unable to change SIGTERM handler\n");
+ return -1;
+ }
+ if(sigaction(SIGQUIT, &newSa, &oldSigQuit) == -1)
+ {
+ if(verbose) fprintf(stderr, "ERROR: unable to change SIGQUIT handler\n");
+ return -1;
+ }
+
+ struct termios oldTerm, newTerm;
+ tcgetattr(fileno(stdin), &oldTerm);
+
+ newTerm = oldTerm;
+
+ newTerm.c_lflag &= ~ECHO;
+ newTerm.c_lflag |= ECHONL;
+
+ tcsetattr(fileno(stdin), TCSAFLUSH, &newTerm);
+
+ printf("NOTE: Try not to use a backspace...\n");
+ printf("Key Passphrase: ");
+ fflush(stdout);
+
+ char c = getchar();
+ size_t i = 0;
+
+ while(c != '\n' && c != '\f' && c != '\r')
+ {
+ if(i < len)
+ {
+ result[i] = c;
+ i++;
+ c = getchar();
+ }
+ else
+ {
+ status = -1;
+ break;
+ }
+ }
+ tcsetattr(fileno(stdin), TCSANOW, &oldTerm);
+
+ if(sigaction(SIGINT, &oldSigInt, NULL) == -1)
+ {
+ if(verbose) fprintf(stderr, "ERROR: unable to change SIGINT handler\n");
+ status = -1;
+ }
+ if(sigaction(SIGTERM, &oldSigTerm, NULL) == -1)
+ {
+ if(verbose) fprintf(stderr, "ERROR: unable to change SIGTERM handler\n");
+ status = -1;
+ }
+ if(sigaction(SIGQUIT, &oldSigQuit, NULL) == -1)
+ {
+ if(verbose) fprintf(stderr, "ERROR: unable to change SIGQUIT handler\n");
+ status = -1;
+ }
+
+ return status;
+}
+
+void closeSftpSession(struct pscp_sftp_session* sftp)
+{
+ if(sftp)
+ {
+ if(sftp->curl)
+ {
+ curl_easy_cleanup(sftp->curl);
+ }
+ if(sftp->url)
+ {
+ free(sftp->url);
+ }
+ free(sftp);
+ }
+}
diff --git a/src/client/pscp_sftp.h b/src/client/pscp_sftp.h
new file mode 100644
index 0000000..fb81e2f
--- /dev/null
+++ b/src/client/pscp_sftp.h
@@ -0,0 +1,32 @@
+/* Copyright 2017 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PSCP_SFTP_H
+#define PSCP_SFTP_H
+
+#include <curl/curl.h>
+#include <stdbool.h>
+
+struct pscp_sftp_session;
+
+int pscp_sftp_global_init();
+struct pscp_sftp_session* startSftpSession(const char * sftp_url, const char * privateKeyPath, bool verbose);
+int sendFileToServer(const struct pscp_sftp_session* sftp, const char * local, const char * remote);
+int getFileFromServer(const struct pscp_sftp_session* sftp, const char * local, const char * remote);
+int pollOnFileFromServer(const struct pscp_sftp_session* sftp, const char * local, const char * remote);
+void closeSftpSession(struct pscp_sftp_session* sftp);
+
+#endif
diff --git a/src/client/pscp_sftp_test.c b/src/client/pscp_sftp_test.c
new file mode 100644
index 0000000..b844184
--- /dev/null
+++ b/src/client/pscp_sftp_test.c
@@ -0,0 +1,67 @@
+/* Copyright 2017 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+#include "pscp_sftp.h"
+
+
+int main(int argc, char** argv)
+{
+ if((argc == 6) )
+ {
+ const char* url = argv[1];
+ const char* key = argv[2];
+ const char* in = argv[3];
+ const char* serverFile = argv[4];
+ const char* out = argv[5];
+ const bool verbose = true;
+ struct pscp_sftp_session* sftp = NULL;
+
+ printf("Running SFTP global init\n");
+ int status = pscp_sftp_global_init();
+ if(status)
+ {
+ printf("Creating sftp session\n");
+ sftp = startSftpSession(url, key, verbose);
+ }
+ if(sftp)
+ {
+ printf("Sending File\n");
+ status = sendFileToServer(sftp, in, serverFile);
+ }
+ else
+ {
+ status = -1;
+ }
+ if(status == 0)
+ {
+ printf("Asking for File\n");
+ status = getFileFromServer(sftp, out, serverFile);
+ }
+ if(sftp)
+ {
+ printf("Closing session...\n");
+ closeSftpSession(sftp);
+ }
+ }
+ else
+ {
+ printf("USAGE:\n %s <url> <private-key-file> <local-in-file> <server-file> <local-out-file> [key-passphrase]\n", argv[0]);
+ }
+
+ return 0;
+}
OpenPOWER on IntegriCloud