Files

859 lines
23 KiB
Go

package main
import (
"archive/zip"
"bufio"
"bytes"
"crypto/sha256"
"crypto/tls"
"encoding/base64"
"flag"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ses"
"github.com/barasher/go-exiftool"
"github.com/fatih/color"
"github.com/google/uuid"
"github.com/pdfcpu/pdfcpu/pkg/api"
"github.com/pdfcpu/pdfcpu/pkg/pdfcpu"
"github.com/sendgrid/sendgrid-go"
"github.com/sendgrid/sendgrid-go/helpers/mail"
"gopkg.in/gomail.v2"
)
var currentDir, _ = os.Getwd()
func main() {
fmt.Println(`
,_
,' '\,_ Utku Sen's
|_,-'_) wholeaked
/##c '\ (
' |' -{. ) "When you have eliminated the impossible,
/\__-' \[] whatever remains, however improbable,
/'-_'\ must be the truth" - Sherlock Holmes
' \
`)
projectName := flag.String("n", "", "Name of the project")
targetsFile := flag.String("t", "", "Path of the targets file")
baseFile := flag.String("f", "", "Path of the base file")
binaryFlag := flag.Bool("binary", true, "Add a unique signature to the binary")
metadataFlag := flag.Bool("metadata", true, "Add a unique signature to metadata of the file")
watermarkFlag := flag.Bool("watermark", true, "Add a unique signature to the PDF file as a watermark")
sendgridFlag := flag.Bool("sendgrid", false, "Send files with Sendgrid Integration")
sesFlag := flag.Bool("ses", false, "Send files with AWS SES Integration")
smtpFlag := flag.Bool("smtp", false, "Send files with a SMTP server")
validateFlag := flag.Bool("validate", false, "Find who leaked the file")
flag.Parse()
if *projectName == "" {
color.Red("Project name (-n) is required.")
flag.PrintDefaults()
os.Exit(1)
}
if *targetsFile == "" && !*validateFlag {
color.Red("Targets file (-t) is required.")
flag.PrintDefaults()
os.Exit(1)
}
if *baseFile == "" {
color.Red("Base file (-f) is required.")
flag.PrintDefaults()
os.Exit(1)
}
if !*binaryFlag && !*metadataFlag && !*watermarkFlag && !*validateFlag {
color.Red("No flags are set")
os.Exit(1)
}
startProcess(*baseFile, *targetsFile, *projectName, *binaryFlag, *metadataFlag, *watermarkFlag, *sendgridFlag, *sesFlag, *smtpFlag, *validateFlag)
}
func startProcess(baseFile, targetsFile, projectName string, binaryFlag, metadataFlag, watermarkFlag, sendgridFlag, sesFlag, smtpFlag, validateFlag bool) {
fmt.Println("Operation started")
projectDir := filepath.Join(currentDir, projectName)
dbPath := filepath.Join(projectDir, "db.csv")
existsFlag := false
if validateFlag {
detectLeak(baseFile, dbPath)
return
}
if _, err := os.Stat(targetsFile); os.IsNotExist(err) {
color.Red("Targets file does not exist.")
os.Exit(1)
}
if _, err := os.Stat(baseFile); os.IsNotExist(err) {
color.Red("Targets file does not exist.")
os.Exit(1)
}
err := os.Mkdir(projectDir, 0777)
if err != nil {
if !sendgridFlag && !sesFlag && !smtpFlag {
color.Red("Project already exists.")
os.Exit(1)
} else {
fmt.Println("Local files are already created for this project. Do you want to send them? (y/n)")
reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println("An error occured while reading input. Please try again", err)
os.Exit(1)
}
input = strings.TrimSuffix(input, "\n")
if input != "y" {
os.Exit(1)
} else {
existsFlag = true
}
}
}
if !existsFlag {
generateTargetDB(dbPath, readTargets(targetsFile))
createLocalFiles(baseFile, projectName, binaryFlag, metadataFlag, watermarkFlag)
color.Magenta("Local files are created")
}
configs := parseConfigFile()
if sendgridFlag {
fmt.Println("Sending files with Sendgrid")
for _, line := range readTargets(dbPath) {
sendWithSendgrid(strings.Split(line, ",")[0], strings.Split(line, ",")[1], configs["FROM_NAME"], configs["FROM_EMAIL"], configs["EMAIL_SUBJECT"], configs["EMAIL_TEMPLATE_PATH"], configs["EMAIL_CONTENT_TYPE"], strings.Split(line, ",")[4])
}
}
if sesFlag {
fmt.Println("Sending files with AWS SES")
for _, line := range readTargets(dbPath) {
sendWithSES(strings.Split(line, ",")[0], strings.Split(line, ",")[1], configs["FROM_NAME"], configs["FROM_EMAIL"], configs["EMAIL_SUBJECT"], configs["EMAIL_TEMPLATE_PATH"], configs["EMAIL_CONTENT_TYPE"], strings.Split(line, ",")[4], configs["AWS_REGION"])
}
}
if smtpFlag {
fmt.Println("Sending files with the SMTP Server")
for _, line := range readTargets(dbPath) {
sendWithSMTP(strings.Split(line, ",")[0], strings.Split(line, ",")[1], configs["FROM_NAME"], configs["FROM_EMAIL"], configs["EMAIL_SUBJECT"], configs["EMAIL_TEMPLATE_PATH"], configs["EMAIL_CONTENT_TYPE"], strings.Split(line, ",")[4])
}
}
fmt.Println("Operation Done. Bye")
}
func detectWatermarkPDF(file, signature string) bool {
operatingSystem := runtime.GOOS
var cmd *exec.Cmd
if operatingSystem == "windows" {
cmd = exec.Command(filepath.Join(currentDir, "pdftotext.exe"), file)
} else {
cmd = exec.Command(filepath.Join(currentDir, "pdftotext"), file)
}
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
color.Red("Couldn't run pdftotext binary")
fmt.Println(err)
os.Exit(1)
}
outPath := strings.ReplaceAll(file, ".pdf", ".txt")
defer os.Remove(outPath)
content, err := ioutil.ReadFile(outPath)
if err != nil {
color.Red("Couldn't read the PDF file")
fmt.Println(err)
os.Exit(1)
}
text := string(content)
return strings.Contains(text, signature)
}
func addWatermarkPDF(file, signature string) {
wm, _ := api.TextWatermark(signature, "sc:.9, rot:0, mo:1, op:0", false, false, pdfcpu.POINTS)
api.AddWatermarksFile(file, "", nil, wm, nil)
}
func detectLeak(file, dbPath string) {
targets := readTargets(dbPath)
foundFlag := false
for _, target := range targets {
signature := strings.Split(target, ",")[2]
name := strings.ReplaceAll(strings.Split(target, ",")[0], " ", "_")
fileHash := strings.Split(target, ",")[3]
binaryFlag, hashFlag, metadataFlag, watermarkFlag := detectSignature(file, signature, fileHash)
if hashFlag {
color.Magenta("File Hash Matched: " + name)
foundFlag = true
}
if binaryFlag {
color.Magenta("Signature Detected in Binary: " + name)
foundFlag = true
}
if metadataFlag {
color.Magenta("Signature Detected in Metadata: " + name)
foundFlag = true
}
if watermarkFlag {
color.Magenta("Watermark Matched: " + name)
foundFlag = true
}
}
if !foundFlag {
fmt.Println("No match found.")
}
}
func getHash(file string) string {
f, err := os.Open(file)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
fmt.Println(err)
os.Exit(1)
}
return fmt.Sprintf("%x", h.Sum(nil))
}
func applySignature(file, signature string, binaryFlag, metadataFlag, watermarkFlag bool) {
extension := filepath.Ext(file)
if extension == ".pdf" && watermarkFlag {
addWatermarkPDF(file, signature)
}
if metadataFlag {
addMetadataSignature(file, signature)
}
if extension != ".docx" && extension != ".xlsx" && extension != ".pptx" {
if binaryFlag {
appendSignature(file, signature)
}
}
}
func appendSignature(file, signature string) {
signature = " " + signature
f, err := os.OpenFile(file, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer f.Close()
_, err = f.WriteString(signature)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func listFiles(root string) ([]string, error) {
var files []string
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
files = append(files, path)
}
return nil
})
if err != nil {
return nil, err
}
return files, nil
}
func addMetadataSignature(file, signature string) {
var metaSection string
extension := filepath.Ext(file)
if extension == ".docx" || extension == ".xlsx" || extension == ".pptx" {
workingDir, _ := filepath.Abs(filepath.Dir(file))
Unzip(file, filepath.Join(workingDir, "temp"))
coreFile := filepath.Join(workingDir, "temp", "docProps", "core.xml")
if _, err := os.Stat(coreFile); os.IsNotExist(err) {
color.Red("Document doesn't contain core.xml metadata section. It usually happens if you use Google Docs. Try to use Microsoft Office instead.")
os.Exit(1)
} else {
f, err := os.Open(coreFile)
if err != nil {
color.Red("Error occurred during reading core.xml file")
fmt.Println(err)
os.Exit(1)
}
defer f.Close()
scanner := bufio.NewScanner(f)
var fileContent string
for scanner.Scan() {
fileContent += scanner.Text()
}
re := regexp.MustCompile(`<dc:creator>([\w\s]+)</dc:creator>`)
match := re.FindStringSubmatch(fileContent)
if match == nil {
color.Red("Document doesn't contain core.xml metadata section. It usually happens if you use Google Docs. Try to use Microsoft Office instead.")
os.Exit(1)
}
newCreator := strings.Replace(fileContent, match[0], "<dc:creator>"+signature+"</dc:creator>", 1)
_ = ioutil.WriteFile(coreFile, []byte(newCreator), 0644)
os.Remove(file)
officeCompress(filepath.Join(workingDir, "temp"), file)
os.RemoveAll(filepath.Join(workingDir, "temp"))
}
} else {
switch {
case extension == ".pdf":
metaSection = "Producer"
case extension == ".mov":
metaSection = "Software"
default:
metaSection = "Title"
}
targetDir := filepath.Dir(file)
tempDir := filepath.Join(targetDir, "temp")
os.Mkdir(filepath.Join(tempDir), 0777)
fileName := filepath.Base(file)
tempFile := filepath.Join(tempDir, fileName)
copyFile(file, tempFile)
e, _ := exiftool.NewExiftool()
defer e.Close()
originals := e.ExtractMetadata(tempFile)
originals[0].SetString(metaSection, signature)
e.WriteMetadata(originals)
os.Remove(file)
copyFile(tempFile, file)
os.RemoveAll(tempDir)
}
}
func officeCompress(source, target string) {
files, err := listFiles(source)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
buf := new(bytes.Buffer)
w := zip.NewWriter(buf)
for _, file := range files {
absPath, _ := filepath.Abs(file)
relPath, _ := filepath.Rel(source, absPath)
f, err := w.Create(relPath)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fh, err := os.Open(absPath)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
_, err = io.Copy(f, fh)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fh.Close()
}
err = w.Close()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
err = ioutil.WriteFile(target, buf.Bytes(), 0644)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func exifRead(file, field string) string {
et, err := exiftool.NewExiftool()
if err != nil {
fmt.Printf("Error when intializing: %v\n", err)
return ""
}
defer et.Close()
fileInfos := et.ExtractMetadata(file)
for _, fileInfo := range fileInfos {
if fileInfo.Err != nil {
fmt.Printf("Error concerning %v: %v\n", fileInfo.File, fileInfo.Err)
continue
}
for k, v := range fileInfo.Fields {
if k == field {
exifValue := fmt.Sprint(v)
return exifValue
}
}
}
return os.DevNull
}
func copyFile(src, dest string) (err error) {
s, err := os.Open(src)
if err != nil {
return err
}
defer s.Close()
d, err := os.Create(dest)
if err != nil {
return err
}
defer d.Close()
_, err = io.Copy(d, s)
if err != nil {
return err
}
return nil
}
func detectSignature(file, signature, hashValue string) (bool, bool, bool, bool) {
binaryFlag := false
hashFlag := false
metadataFlag := false
watermarkFlag := false
fileHash := getHash(file)
if fileHash == hashValue {
hashFlag = true
}
var metaSection string
f, err := os.Open(file)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer f.Close()
var lines []string
scanner := bufio.NewScanner(f)
const maxCapacity = 5048 * 5048
buf := make([]byte, maxCapacity)
scanner.Buffer(buf, maxCapacity)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Println(err)
os.Exit(1)
}
for _, line := range lines {
if strings.Contains(line, signature) {
binaryFlag = true
}
}
extension := filepath.Ext(file)
switch {
case extension == ".pdf":
metaSection = "Producer"
watermarkFlag = detectWatermarkPDF(file, signature)
case extension == ".mov":
metaSection = "Software"
case extension == ".docx":
metaSection = "Creator"
case extension == ".xlsx":
metaSection = "Creator"
case extension == ".pptx":
metaSection = "Creator"
default:
metaSection = "Title"
}
exifValue := exifRead(file, metaSection)
metadataFlag = strings.Contains(exifValue, signature)
return binaryFlag, hashFlag, metadataFlag, watermarkFlag
}
func readTargets(file string) []string {
f, err := os.Open(file)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer f.Close()
var lines []string
scanner := bufio.NewScanner(f)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Println(err)
os.Exit(1)
}
return lines
}
func generateSignature() string {
id := uuid.New()
return "75746b7573656e-" + id.String()
}
func generateTargetDB(file string, targets []string) {
f, err := os.Create(file)
if err != nil {
color.Red("Can't create the database file")
fmt.Println(err)
os.Exit(1)
}
defer f.Close()
for _, target := range targets {
if target != "" {
if !strings.Contains(target, ",") || strings.Count(target, ",") > 1 {
color.Red("Wrong target format: " + target)
color.Red("It should be something like this: Utku Sen,utku@utkusen.com")
os.Exit(1)
}
_, err := f.WriteString(target + "," + generateSignature() + "\n")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
}
}
func createLocalFiles(baseFile, projectName string, binaryFlag, metadataFlag, watermarkFlag bool) {
currentDir, _ := os.Getwd()
projectDir := filepath.Join(currentDir, projectName)
fileDir := filepath.Join(projectDir, "files")
targets := readTargets(filepath.Join(projectDir, "db.csv"))
err := os.Mkdir(fileDir, 0777)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
var updatedDB string
var fileHash string
for _, target := range targets {
if target != "" {
signature := strings.Split(target, ",")[2]
name := strings.ReplaceAll(strings.Split(target, ",")[0], " ", "_")
privateDir := filepath.Join(fileDir, name)
err := os.Mkdir(privateDir, 0777)
if err != nil {
color.Red("Can't create the folder")
fmt.Println(err)
os.Exit(1)
}
fileLocation := filepath.Join(privateDir, filepath.Base(baseFile))
_ = CopyTargetFile(baseFile, fileLocation)
applySignature(fileLocation, signature, binaryFlag, metadataFlag, watermarkFlag)
fileHash = getHash(fileLocation)
updatedDB += target + "," + fileHash + "," + fileLocation + "\n"
}
err = ioutil.WriteFile(filepath.Join(projectDir, "db.csv"), []byte(updatedDB), 0644)
if err != nil {
color.Red("Can't write to the database")
fmt.Println(err)
os.Exit(1)
}
}
}
func CopyTargetFile(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, in)
cerr := out.Close()
if err != nil {
return err
}
return cerr
}
func Unzip(src, destination string) ([]string, error) {
var filenames []string
r, err := zip.OpenReader(src)
if err != nil {
return filenames, err
}
defer r.Close()
for _, f := range r.File {
fpath := filepath.Join(destination, f.Name)
if !strings.HasPrefix(fpath, filepath.Clean(destination)+string(os.PathSeparator)) {
return filenames, fmt.Errorf("%s is an illegal filepath", fpath)
}
filenames = append(filenames, fpath)
if f.FileInfo().IsDir() {
os.MkdirAll(fpath, os.ModePerm)
continue
}
if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
return filenames, err
}
outFile, err := os.OpenFile(fpath,
os.O_WRONLY|os.O_CREATE|os.O_TRUNC,
f.Mode())
if err != nil {
return filenames, err
}
rc, err := f.Open()
if err != nil {
return filenames, err
}
_, err = io.Copy(outFile, rc)
outFile.Close()
rc.Close()
if err != nil {
return filenames, err
}
}
return filenames, nil
}
func GetFileContentType(out *os.File) string {
buffer := make([]byte, 512)
_, err := out.Read(buffer)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
contentType := http.DetectContentType(buffer)
return contentType
}
func sendWithSendgrid(toName, toEmail, fromName, fromEmail, subject, bodyFile, contentType, attachment string) {
configFile, err := os.Open("CONFIG")
if err != nil {
color.Red("Can't read the CONFIG file")
fmt.Println(err)
os.Exit(1)
}
defer configFile.Close()
scanner := bufio.NewScanner(configFile)
for scanner.Scan() {
if strings.Contains(scanner.Text(), "SENDGRID_API_KEY") {
apiKey := strings.TrimSpace(strings.ReplaceAll(strings.Split(scanner.Text(), "=")[1], "\"", ""))
if apiKey == "" {
fmt.Println("No Sendgrid API Key is set in the CONFIG file")
os.Exit(1)
} else {
body, err := ioutil.ReadFile(bodyFile)
if err != nil {
color.Red("Error occurred while reading the template file")
fmt.Println(err)
os.Exit(1)
}
m := mail.NewV3Mail()
newBody := strings.ReplaceAll(string(body), "{{Name}}", toName)
from := mail.NewEmail(fromName, fromEmail)
to := mail.NewEmail(toName, toEmail)
content := mail.NewContent("text/plain", newBody)
if contentType == "text" {
content = mail.NewContent("text/plain", newBody)
} else if contentType == "html" {
content = mail.NewContent("text/html", newBody)
} else {
fmt.Println("Content type is not set correctly")
os.Exit(1)
}
m.SetFrom(from)
m.AddContent(content)
personalization := mail.NewPersonalization()
personalization.AddTos(to)
personalization.Subject = subject
m.AddPersonalizations(personalization)
f, err := os.Open(attachment)
if err != nil {
color.Red("Can't open the attachment file")
fmt.Println(err)
os.Exit(1)
}
contentType := GetFileContentType(f)
f.Close()
attachmentFile := mail.NewAttachment()
dat, err := ioutil.ReadFile(attachment)
if err != nil {
fmt.Println(err)
}
encoded := base64.StdEncoding.EncodeToString([]byte(dat))
attachmentFile.SetContent(encoded)
attachmentFile.SetType(contentType)
fileName := filepath.Base(attachment)
attachmentFile.SetFilename(fileName)
attachmentFile.SetDisposition("attachment")
m.AddAttachment(attachmentFile)
request := sendgrid.GetRequest(apiKey, "/v3/mail/send", "https://api.sendgrid.com")
request.Method = "POST"
request.Body = mail.GetRequestBody(m)
response, err := sendgrid.API(request)
if err != nil {
color.Red("Error occurred while using Sendgrid's API")
fmt.Println(err)
os.Exit(1)
}
if response.StatusCode == 202 {
color.Green("Email sent successfully to : " + toName + " (" + toEmail + ")")
} else {
color.Red("Email couldn't sent to : " + toName + " (" + toEmail + ") Check your Sendgrid Integration:")
fmt.Println(response)
os.Exit(1)
}
}
}
}
}
func sendWithSES(toName, toEmail, fromName, fromEmail, subject, bodyFile, contentType, attachment, region string) {
sess, err := session.NewSession(&aws.Config{
Region: aws.String(region)},
)
if err != nil {
color.Red("Error occurred while creating AWS session")
fmt.Println(err)
os.Exit(1)
}
body, err := ioutil.ReadFile(bodyFile)
if err != nil {
color.Red("Error occurred while reading the template file")
fmt.Println(err)
os.Exit(1)
}
newBody := strings.ReplaceAll(string(body), "{{Name}}", toName)
msg := gomail.NewMessage()
svc := ses.New(sess)
msg.SetAddressHeader("From", fromEmail, fromName)
type Recipient struct {
toEmails []string
}
recipient := Recipient{
toEmails: []string{toEmail},
}
var recipients []*string
for _, r := range recipient.toEmails {
recipient := r
recipients = append(recipients, &recipient)
}
msg.SetHeader("To", recipient.toEmails...)
msg.SetHeader("Subject", subject)
if contentType == "text" {
msg.SetBody("text/plain", newBody)
} else if contentType == "html" {
msg.SetBody("text/html", newBody)
} else {
fmt.Println("Content type is not set correctly")
os.Exit(1)
}
msg.Attach(attachment)
var emailRaw bytes.Buffer
msg.WriteTo(&emailRaw)
message := ses.RawMessage{Data: emailRaw.Bytes()}
input := &ses.SendRawEmailInput{Source: &fromEmail, Destinations: recipients, RawMessage: &message}
_, err = svc.SendRawEmail(input)
if err != nil {
color.Red("Email couldn't sent to : " + toName + " (" + toEmail + ")")
fmt.Println(err)
os.Exit(1)
}
color.Green("Email sent successfully to : " + toName + " (" + toEmail + ")")
}
func sendWithSMTP(toName, toEmail, fromName, fromEmail, subject, bodyFile, contentType, attachment string) {
configs := parseConfigFile()
if configs["SMTP_SERVER"] == "" {
color.Red("No SMTP Server is set in the CONFIG file")
os.Exit(1)
}
if configs["SMTP_PORT"] == "" {
color.Red("No SMTP Port is set in the CONFIG file")
os.Exit(1)
}
m := gomail.NewMessage()
m.SetHeader("From", m.FormatAddress(fromEmail, fromName))
m.SetHeader("To", toEmail)
m.SetHeader("Subject", subject)
body, err := ioutil.ReadFile(bodyFile)
if err != nil {
color.Red("Error occurred while reading the template file")
fmt.Println(err)
os.Exit(1)
}
newBody := strings.ReplaceAll(string(body), "{{Name}}", toName)
if contentType == "text" {
m.SetBody("text/plain", newBody)
} else if contentType == "html" {
m.SetBody("text/html", newBody)
} else {
color.Red("Content type is not set correctly")
os.Exit(1)
}
m.Attach(attachment)
intPort, err := strconv.Atoi(configs["SMTP_PORT"])
if err != nil {
color.Red("Invalid SMTP Port")
os.Exit(1)
}
d := gomail.NewDialer(configs["SMTP_SERVER"], intPort, configs["SMTP_USERNAME"], configs["SMTP_PASSWORD"])
d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
if err := d.DialAndSend(m); err != nil {
color.Red("Email couldn't sent to : " + toName + " (" + toEmail + ")")
fmt.Println(err)
os.Exit(1)
} else {
color.Green("Email sent successfully to : " + toName + " (" + toEmail + ")")
}
}
func parseConfigFile() map[string]string {
f, err := os.Open("CONFIG")
if err != nil {
color.Red("Can't read the CONFIG file")
fmt.Println(err)
os.Exit(1)
}
defer f.Close()
scanner := bufio.NewScanner(f)
var config = make(map[string]string)
for scanner.Scan() {
if strings.Contains(scanner.Text(), "=") {
key := strings.TrimSpace(strings.Split(scanner.Text(), "=")[0])
value := strings.TrimSpace(strings.ReplaceAll(strings.Split(scanner.Text(), "=")[1], "\"", ""))
config[key] = value
}
}
return config
}