Skip to content

Commit

Permalink
Add initial implementation of file transfer utility with configuratio…
Browse files Browse the repository at this point in the history
…n and database support
  • Loading branch information
ndonathan committed Nov 4, 2024
1 parent 1334a21 commit 0e54645
Show file tree
Hide file tree
Showing 9 changed files with 1,183 additions and 2 deletions.
74 changes: 72 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,72 @@
# CLI_FileTransfer
CLI app written in Go. Acts as an orchestrator and wrapper for multiple utilities.
# CLI File Transfer Utility

⚠️ **WARNING: This project is in pre-alpha stage and not ready for production use** ⚠️

## Description

A command-line utility for transferring files using various protocols such as Azure Blob Storage, Amazon S3, SFTP, CIFS, and local file transfers. The project aims to provide a simple, unified interface for file transfers across different protocols.

## Current Status

This project is under active development and many features are incomplete or not fully tested.

### Working Features
- Basic TUI (Terminal User Interface) with protocol selection
- Configuration file support
- Local file transfer implementation
- Basic database logging of transfers
- Protocol framework for multiple transfer types

### In Progress
- SFTP implementation (partial)
- Azure Blob Storage implementation (partial)
- S3 implementation (partial)
- CIFS/SMB implementation (not started)
- Error handling improvements
- Progress reporting
- Transfer validation
- Credential management
- Unit tests

### Known Issues
- Authentication not fully implemented for remote protocols
- No progress indication during transfers
- Error handling needs improvement
- Configuration validation missing
- No retry mechanism for failed transfers
- Missing proper logging system
- Security considerations need review

## Supported Protocols (Planned)

- Azure Blob Storage (`azureblob`) - Partial
- CIFS/SMB (`cifs`) - Not implemented
- SFTP (`sftp`) - Partial
- Amazon S3 (`s3`) - Partial
- Local file system (`local`) - Working

## Installation

## Usage
Usage
Interactive Mode (Recommended)
This will start the Terminal User Interface (TUI) where you can:

Select a transfer protocol using arrow keys
Enter source path
Enter destination path
Command Line Mode
./CLI_FileTransfer -protocol <protocol> -source <source_path> -destination <dest_path>

# Examples:
# Local file copy
./CLI_FileTransfer -protocol local -source ./myfile.txt -destination ./backup/myfile.txt

# SFTP transfer
./CLI_FileTransfer -protocol sftp -source ./localfile.txt -destination /remote/path/file.txt

# S3 upload
./CLI_FileTransfer -protocol s3 -source ./myfile.txt -destination bucket/myfile.txt

# Azure Blob transfer
./CLI_FileTransfer -protocol azureblob -source ./myfile.txt -destination container/myfile.txt
29 changes: 29 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package main

import (
"github.com/spf13/viper"
"log"
)

// initConfig initializes the configuration by reading the config file.
func initConfig() {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")

// Default configurations
viper.SetDefault("azure.accountName", "")
viper.SetDefault("azure.accountKey", "")
viper.SetDefault("azure.containerName", "")
viper.SetDefault("aws.bucket", "")
viper.SetDefault("aws.region", "")
viper.SetDefault("cifs.mountPoint", "")
viper.SetDefault("sftp.host", "")
viper.SetDefault("sftp.port", "22")
viper.SetDefault("sftp.username", "")
viper.SetDefault("sftp.password", "")

if err := viper.ReadInConfig(); err != nil {
log.Fatalf("Error reading config file: %v", err)
}
}
21 changes: 21 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
sftp:
username: "your_username"
password: "your_password"
host: "sftp.example.com"
port: 22

azureblob:
account_name: "your_account_name"
account_key: "your_account_key"
container_name: "your_container"

aws:
bucket: "your-bucket-name"
region: "us-west-2"
access_key: "your-access-key"
secret_key: "your-secret-key"

cifs:
mount_point: "/mnt/share"
username: "domain\\user"
password: "your_password"
40 changes: 40 additions & 0 deletions database.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

import (
"database/sql"
_ "github.com/mattn/go-sqlite3"
)

// initDatabase initializes the SQLite database.
func initDatabase() (*sql.DB, error) {
db, err := sql.Open("sqlite3", "./transfers.db")
if (err != nil) {
return nil, err
}

sqlStmt := `
CREATE TABLE IF NOT EXISTS transfers (
id INTEGER NOT NULL PRIMARY KEY,
protocol TEXT,
source TEXT,
destination TEXT
);`
_, err = db.Exec(sqlStmt)
if err != nil {
return nil, err
}

return db, nil
}

// logTransfer logs the transfer details into the database.
func logTransfer(db *sql.DB, protocol, source, destination string) error {
stmt, err := db.Prepare("INSERT INTO transfers(protocol, source, destination) values(?,?,?)")
if err != nil {
return err
}
defer stmt.Close()

_, err = stmt.Exec(protocol, source, destination)
return err
}
200 changes: 200 additions & 0 deletions file_transfer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
package main

import (
"context"
"fmt"
"io"
"log"
"net/url"
"os"
"path/filepath"

"github.com/Azure/azure-storage-blob-go/azblob"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/pkg/sftp"
"github.com/spf13/viper"
"golang.org/x/crypto/ssh"
)

// transferFile transfers a file using the specified protocol.
// Supported protocols: azureblob, cifs, sftp, s3, local.
func transferFile(protocol, source, destination string) error {
switch protocol {
case "azureblob":
return transferAzureBlob(source, destination)
case "cifs":
return transferCIFS(source, destination)
case "sftp":
return transferSFTP(source, destination)
case "s3":
return transferS3(source, destination)
case "local":
return transferLocal(source, destination)
default:
return fmt.Errorf("unsupported protocol: %s", protocol)
}
}

// transferAzureBlob uploads a file to Azure Blob Storage.
func transferAzureBlob(source, destination string) error {
log.Println("Transferring via Azure Blob Storage")

accountName := viper.GetString("azure.accountName")
accountKey := viper.GetString("azure.accountKey")
containerName := viper.GetString("azure.containerName")

if accountName == "" || accountKey == "" || containerName == "" {
return fmt.Errorf("Azure storage credentials not provided")
}

credential, err := azblob.NewSharedKeyCredential(accountName, accountKey)
if err != nil {
return fmt.Errorf("failed to create Azure credential: %v", err)
}

pipeline := azblob.NewPipeline(credential, azblob.PipelineOptions{})
url, _ := url.Parse(
fmt.Sprintf("https://%s.blob.core.windows.net/%s", accountName, containerName))

containerURL := azblob.NewContainerURL(*url, pipeline)
blobURL := containerURL.NewBlockBlobURL(filepath.Base(destination))

file, err := os.Open(source)
if err != nil {
return fmt.Errorf("failed to open source file: %v", err)
}
defer file.Close()

ctx := context.Background()
_, err = azblob.UploadFileToBlockBlob(ctx, file, blobURL, azblob.UploadToBlockBlobOptions{})
if err != nil {
return fmt.Errorf("failed to upload file to Azure Blob: %v", err)
}

log.Println("File transferred via Azure Blob Storage successfully")
return nil
}

// transferCIFS copies a file to a CIFS (SMB) network share.
func transferCIFS(source, destination string) error {
log.Println("Transferring via CIFS")

mountPoint := viper.GetString("cifs.mountPoint")
if mountPoint == "" {
return fmt.Errorf("CIFS mount point not configured")
}

destPath := filepath.Join(mountPoint, destination)
return transferLocal(source, destPath)
}

// transferSFTP uploads a file via SFTP.
func transferSFTP(source, destination string) error {
// SSH client configuration
config := &ssh.ClientConfig{
User: "username",
Auth: []ssh.AuthMethod{
ssh.Password("password"),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}

// Connect to the SSH server
sshClient, err := ssh.Dial("tcp", "sftp.example.com:22", config)
if (err != nil) {
return fmt.Errorf("failed to dial: %v", err)
}
defer sshClient.Close()

// Create new SFTP client
sftpClient, err := sftp.NewClient(sshClient)
if (err != nil) {
return fmt.Errorf("failed to create sftp client: %v", err)
}
defer sftpClient.Close()

// Open source file
srcFile, err := os.Open(source)
if (err != nil) {
return fmt.Errorf("failed to open source file: %v", err)
}
defer srcFile.Close()

// Create destination file on the server
dstFile, err := sftpClient.Create(destination)
if (err != nil) {
return fmt.Errorf("failed to create destination file: %v", err)
}
defer dstFile.Close()

// Copy data from source file to destination file
_, err = io.Copy(dstFile, srcFile)
if (err != nil) {
return fmt.Errorf("failed to copy file: %v", err)
}

log.Println("File transferred via SFTP successfully")
return nil
}

// transferS3 uploads a file to Amazon S3.
func transferS3(source, destination string) error {
log.Println("Transferring via Amazon S3")

bucket := viper.GetString("aws.bucket")
region := viper.GetString("aws.region")

if bucket == "" || region == "" {
return fmt.Errorf("AWS S3 bucket or region not configured")
}

sess, err := session.NewSession(&aws.Config{Region: aws.String(region)})
if err != nil {
return fmt.Errorf("failed to create AWS session: %v", err)
}

uploader := s3.New(sess)
file, err := os.Open(source)
if err != nil {
return fmt.Errorf("failed to open source file: %v", err)
}
defer file.Close()

_, err = uploader.PutObject(&s3.PutObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(destination),
Body: file,
})
if err != nil {
return fmt.Errorf("failed to upload to S3: %v", err)
}

log.Println("File transferred via Amazon S3 successfully")
return nil
}

// transferLocal copies a file locally.
func transferLocal(source, destination string) error {
// Copying file from source to destination locally
inputFile, err := os.Open(source)
if err != nil {
return fmt.Errorf("failed to open source file: %v", err)
}
defer inputFile.Close()

outputFile, err := os.Create(destination)
if err != nil {
return fmt.Errorf("failed to create destination file: %v", err)
}
defer outputFile.Close()

_, err = io.Copy(outputFile, inputFile)
if err != nil {
return fmt.Errorf("failed to copy file: %v", err)
}

log.Println("File transferred locally successfully")
return nil
}
Loading

0 comments on commit 0e54645

Please sign in to comment.