This repository has been archived on 2022-02-10. You can view files and clone it, but cannot push or open issues or pull requests.
rangitaki-sync/rangitaki-sync.c

354 lines
9.2 KiB
C

/*
* Rangitaki Sync Library
*
* A program for downloading and uploading files over ssh.
* Written for the Rangitaki blogging engine.
*
* Proudly written in C and with use of libssh (libssh.org)
*
* Version: 0.1
*
* COPYRIGHT (c) 2015 - 2016 The Rangitaki Project
* COPYRIGHT (c) 2015 - 2016 Marcel Kapfer (mmk2410)
* <marcelmichaelkapfer@yahoo.co.nz>
*
* MIT License
*
*/
#include <libssh/libssh.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include "rangitaki-sync.h"
#include "dbg.h"
#define MAX_FILE_LENGTH 255
char * combine(char *begin, char *end){
char *result;
check(begin != NULL, "Begin can't be NULL.");
check(end != NULL, "End can't be NULL.");
size_t begin_length = strnlen(begin, MAX_FILE_LENGTH - 1);
size_t end_length = strnlen(end, MAX_FILE_LENGTH - 1);
assert(begin_length < MAX_FILE_LENGTH && "Got the begin too long.");
assert(end_length < MAX_FILE_LENGTH && "Got the end to long.");
result = malloc(begin_length + end_length + 1);
check(result != NULL, "Failed to define result");
strncpy(result, begin, MAX_FILE_LENGTH);
check(result != NULL, "Failed to copy the string");
strncat(result, end, MAX_FILE_LENGTH * 2);
int rc = strncmp(result, begin, MAX_FILE_LENGTH * 2);
check(rc <= 0, "Failed to concat string.");
return result;
error:
if (result) free(result);
return NULL;
}
char * getFilename(char *input)
{
check(input != NULL, "Input is NULL.");
char* string;
string = strdup(input);
int buffer[100];
int i = 0;
int j = 0;
for(i = 0; i <= strlen(string); i++) {
if(string[i] == '/') {
buffer[j] = i;
j++;
}
}
j--;
int size = strlen(string) - buffer[j]; // Includes the \0
char *result = malloc(size);
strcpy(result, &string[buffer[j]+1]);
result[size] = '\0';
free(string);
return result;
error:
return NULL;
}
void ssh_initialize(const char *host, const char *user, const char *password,
const char *remote_dir, const char *local_dir,
const unsigned int port, ssh_data *data)
{
data->host = strdup(host);
data->user = strdup(user);
data->password = strdup(password);
data->remote_dir = strdup(remote_dir);
data->local_dir = strdup(local_dir);
data->port = port;
data->verbosity = SSH_LOG_PROTOCOL;
}
int ssh_server_auth(ssh_session ssh)
{
char *hexa;
int state;
unsigned char *hash = NULL;
size_t hlen;
ssh_key srv_pubkey;
int rc;
state = ssh_is_server_known(ssh);
rc = ssh_get_publickey(ssh, &srv_pubkey);
check(rc >= 0, "Couldn't not authenticate the server.");
rc = ssh_get_publickey_hash(srv_pubkey,
SSH_PUBLICKEY_HASH_SHA1,
&hash,
&hlen);
ssh_key_free(srv_pubkey);
check(rc >= 0, "Couldn't not authenticate the server.");
switch (state)
{
case SSH_SERVER_KNOWN_OK:
log_info("Found host key");
break;
case SSH_SERVER_KNOWN_CHANGED:
log_err("The host key of the server changed. Aborting connection.");
ssh_print_hexa("Public key hash", hash, hlen);
ssh_clean_pubkey_hash(&hash);
return -1;
case SSH_SERVER_FOUND_OTHER:
log_err("The host key for this server was not found but an other. Aborting connection.");
ssh_clean_pubkey_hash(&hash);
return -1;
case SSH_SERVER_FILE_NOT_FOUND:
log_warn("Couldn't find known hosts file.");
case SSH_SERVER_NOT_KNOWN:
hexa = ssh_get_hexa(hash, hlen);
log_info("Adding %s to known hosts.", hexa);
free(hexa);
if (ssh_write_knownhost(ssh) < 0) {
log_err("Error adding server to known hosts file: %s", strerror(errno));
ssh_clean_pubkey_hash(&hash);
return -1;
}
break;
default:
log_err("%s", ssh_get_error(ssh));
ssh_clean_pubkey_hash(&hash);
return -1;
}
ssh_clean_pubkey_hash(&hash);
return 0;
error:
ssh_clean_pubkey_hash(&hash);
return -1;
}
ssh_session ssh_open(ssh_data *data)
{
int rc = 0;
ssh_session ssh = ssh_new();
if(ssh == NULL) {
log_err("Failed th create ssh session.");
return NULL;
}
if(ssh_options_set(ssh, SSH_OPTIONS_HOST, data->host) < 0) {
ssh_free(ssh);
log_err("Failed to set host.");
return NULL;
}
if(ssh_options_set(ssh, SSH_OPTIONS_USER, data->user) < 0) {
ssh_free(ssh);
log_err("Failed to set user.");
return NULL;
}
if(ssh_options_set(ssh, SSH_OPTIONS_PORT, &data->port)) {
ssh_free(ssh);
log_err("Failed to set port.");
return NULL;
}
if(ssh_options_set(ssh, SSH_OPTIONS_LOG_VERBOSITY, &data->verbosity)) {
ssh_free(ssh);
log_err("Failed to set verbosity.");
return NULL;
}
if(ssh_connect(ssh)) {
log_err("Connection failed: %s.", ssh_get_error(ssh));
ssh_disconnect(ssh);
ssh_free(ssh);
return NULL;
}
if(ssh_server_auth < 0) {
log_err("Couldn't authenticate the server.");
ssh_disconnect(ssh);
ssh_free(ssh);
return NULL;
}
rc = ssh_userauth_password(ssh, data->user, data->password);
if(rc == SSH_AUTH_SUCCESS) {
return ssh;
} else if(rc == SSH_AUTH_DENIED) {
log_err("Authentication failed.");
} else {
log_err("Error while authenticating");
}
return NULL;
}
int ssh_close(ssh_session ssh)
{
ssh_disconnect(ssh);
ssh_free(ssh);
return 0;
}
ssh_scp scp_open(ssh_session ssh, ssh_data *data, int mode)
{
int rc = 0;
ssh_scp scp = ssh_scp_new(ssh, mode, data->remote_dir);
check(scp != NULL, "Failed to create the scp session: %s", ssh_get_error(ssh));
rc = ssh_scp_init(scp);
if(rc == -1)
{
log_err("Failed to initialize the scp connection.");
goto error;
}
check(rc == SSH_OK, "Failed to initialize the scp session: %s", ssh_get_error(ssh));
return scp;
error:
if(scp) ssh_scp_free(scp);
return NULL;
}
int scp_close(ssh_scp scp)
{
ssh_scp_close(scp);
ssh_scp_free(scp);
return 0;
}
int scp_download(ssh_session ssh, ssh_scp scp, ssh_data *data)
{
int rc;
int rv;
int size, mode;
char buffer[16384];
char *local_dir = strdup(data->local_dir);
do {
rc = ssh_scp_pull_request(scp);
switch (rc) {
case SSH_SCP_REQUEST_NEWFILE:
size = ssh_scp_request_get_size(scp);
mode = ssh_scp_request_get_permissions(scp);
char *filename = strdup(ssh_scp_request_get_filename(scp));
check(filename != NULL, "Couldn't get the filename.");
printf("DOWNLOAD: %s %d %d\n", filename, size, mode);
char *dir = combine(local_dir, filename);
FILE *output = fopen(dir, "w+");
check(output != NULL, "FILE couldn't be created");
ssh_scp_accept_request(scp);
rc = ssh_scp_read(scp, buffer, sizeof(buffer));
check(rc != SSH_ERROR, "Error downloading the file. %s",
ssh_get_error(ssh));
rv = fwrite(buffer, size, 1, output);
check(rv != 0, "Failed to write file");
rv = fclose(output);
free(dir);
free(filename);
check(rv == 0, "Failed to close FILE");
printf("Done.\n");
break;
case SSH_ERROR:
log_err("Error: %s", ssh_get_error(ssh));
goto error;
case SSH_SCP_REQUEST_WARNING:
log_warn("Warning: %s", ssh_scp_request_get_warning(scp));
break;
case SSH_SCP_REQUEST_NEWDIR:
filename = strdup(ssh_scp_request_get_filename(scp));
mode = ssh_scp_request_get_permissions(scp);
printf("DOWNLOAD: %s %d\n", filename, mode);
ssh_scp_accept_request(scp);
break;
case SSH_SCP_REQUEST_ENDDIR:
break;
case SSH_SCP_REQUEST_EOF:
log_info("DONE");
return 0;
}
} while (1);
error:
return -1;
}
int scp_upload(ssh_session ssh, ssh_scp scp, ssh_data *data)
{
int rc = 0;
FILE *fisz = fopen(data->local_dir, "rb");
fseek(fisz, 0L, SEEK_END);
int size = ftell(fisz);
fclose(fisz);
rc = ssh_scp_push_file64(scp, getFilename(data->local_dir), size,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
check(rc == SSH_OK, "Failed to create file: %s", ssh_get_error(ssh));
char *buffer = malloc(size + 100);
FILE *file = fopen(data->local_dir, "r");
check(file != NULL, "Error opening local file.");
rc = 0;
rc = fread(buffer, size, 1, file);
rc = ssh_scp_write(scp, buffer, size);
check(rc == SSH_OK, "Error while writing remote file: %s", ssh_get_error(ssh));
return 0;
error:
return -1;
}