348 lines
9.5 KiB
C
348 lines
9.5 KiB
C
|
/* Rangitaki Sync Library
|
||
|
*
|
||
|
* A program for downloading and uploading blog posts,
|
||
|
* blogs file and media files from a rangitaki blog.
|
||
|
*
|
||
|
* Proudly written in C and with use of libssh (libssh.org)
|
||
|
*
|
||
|
* Version: 0.1
|
||
|
*
|
||
|
* Authors:
|
||
|
*
|
||
|
* COPYRIGHT (c) 2015 The Rangitaki Project
|
||
|
* COPYRIGHT (c) 2015 Marcel Kapfer (mmk2410) <marcelmichaelkapfer@yahoo.co.nz>
|
||
|
*
|
||
|
* License: MIT License
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||
|
* a copy of this software and associated documentation files (the
|
||
|
* "Software"), to deal in the Software without restriction, including
|
||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||
|
* the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be
|
||
|
* included in all copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <libssh/libssh.h>
|
||
|
#include "rangitaki-sync.h"
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <errno.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "dbg.h"
|
||
|
|
||
|
char * combine(const char * begin, char * end){
|
||
|
char * result = malloc(strlen(begin) + strlen(end) + 1);
|
||
|
strcpy(result, begin);
|
||
|
strcat(result, end);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
char * getFilename(char *input)
|
||
|
{
|
||
|
check(input != NULL, "Invalid input");
|
||
|
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];
|
||
|
const 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;
|
||
|
}
|