/* Description: Tool to save bookmarks. Auth0r: sml@lacashita.com Use it only as educational purpose. */ package main import ( "flag" "fmt" "database/sql" "log" "os" "github.com/fatih/color" _ "github.com/mattn/go-sqlite3" "github.com/rodaine/table" "strconv" "time" "github.com/chromedp/chromedp" "crypto/md5" "encoding/hex" "io/ioutil" "context" ) func check(e error) { if e != nil { panic(e) } } const help = ` (ยท)_(o) babil0nia-kli Basic help ----------- Add url: bkli add -u https://lacashita.com -d "Cool blog" Add url with tags: bkli add -u https://lacashita.com -d "Cool blog" -t linux,coding,go List url by tag: bkli list -t linux List url by word: bkli list -w myword List all urls: bkli list List url by status: bkli list -s Open Delete url: bkli delete Modify tags: bkli mod -t hacking,testing Modify description : bkli mod -d "My new description" Download Url: bkli download View image if downloaded: bkli view Check more help in README.md file. ` func main() { // Get babil0nia_DB env, if doesnt exists create ~/babil0nia.db as database. var filedb string var homepath = os.Getenv("HOME") if os.Getenv("babil0nia_DB") == "" { filedb = homepath+"/babil0nia.db" } else { filedb = os.Getenv("babil0nia_DB") } // Create ~/.babil0niafiles folder to save screenshots if _, err := os.Stat(homepath+"/.babil0niafiles"); os.IsNotExist(err) { err = os.Mkdir(homepath+"/.babil0niafiles", 0755) if err != nil { log.Fatal(err) } } checkDB(filedb) bklimain(len(os.Args),filedb) } type Url struct { id *int date *string url *string description *string tags *string screenshot *string } // Main function. func bklimain(arguments int,filedb string){ var u Url var existid int sqlDB, _ := sql.Open("sqlite3",filedb) defer sqlDB.Close() if len(os.Args) < 2 { fmt.Println(help) os.Exit(0) } switch os.Args[1] { case "add": addCommand := flag.NewFlagSet("add", flag.ExitOnError) u.url = addCommand.String("u", "", "Url") u.description = addCommand.String("d", "", "Description") u.tags = addCommand.String("t", "", "Tags") addCommand.Parse(os.Args[2:]) var verify int if *u.url == "" || *u.description == "" { fmt.Println("Parameter -u or -d should not be empty") os.Exit(0) } else { // Check if the URL to add exists. verify = checkDupURL(sqlDB,*u.url) if verify == 0 { addUrl(sqlDB,*u.url, *u.description, *u.tags) } else { fmt.Println("URL exists.") os.Exit(0) } } case "list": listCommand := flag.NewFlagSet("list", flag.ExitOnError) u.description = listCommand.String("w", "%", "Word") u.tags = listCommand.String("t", "%", "Tag") listCommand.Parse(os.Args[2:]) listUrl(sqlDB,*u.description,*u.tags) case "mod": if len(os.Args) > 2{ modCommand := flag.NewFlagSet("mod", flag.ExitOnError) u.url = modCommand.String("u", "", "URL") u.description = modCommand.String("d", "", "Description") u.tags = modCommand.String("t", "", "Tags") modCommand.Parse(os.Args[3:]) theintid, err := strconv.Atoi(os.Args[2]) if err != nil { fmt.Println("Parameter should be the ID.") os.Exit(0) } existid = checkID(sqlDB,theintid) if existid != 0 { modURL(sqlDB,*u.url,*u.description,*u.tags,theintid) } else { fmt.Println("ID dont exist.") } } else { fmt.Println("Usage: bkli mod ") } case "download": if len(os.Args) > 2{ // Convert param 2 into int. theintid, err := strconv.Atoi(os.Args[2]) if err != nil { fmt.Println("Parameter should be the ID.") os.Exit(0) } existid = checkID(sqlDB,theintid) if existid != 0 { urltodownload := obtainURL(sqlDB,theintid) downloadedfile := checkDownload(sqlDB,urltodownload) filename := getFilename(urltodownload) var homepath = os.Getenv("HOME") screenshotfullpath := homepath+"/.babil0niafiles/"+filename+".jpg" screenshotname := filename+".jpg" if downloadedfile == false { err2 := archiveURL(urltodownload,screenshotfullpath,screenshotname,sqlDB) if err2 != nil { panic(err2) } } else { fmt.Println("URL downloaded in the past.") } } else { fmt.Println("ID dont exist.") } } else { fmt.Println("Usage: bkli download ") } case "delete": if len(os.Args) > 2{ // Convert param 2 into int. theintid, err := strconv.Atoi(os.Args[2]) if err != nil { fmt.Println("Parameter should be the ID.") os.Exit(0) } existid = checkID(sqlDB,theintid) if existid != 0 { filetodel := obtainDownload(sqlDB,theintid) if filetodel != "" { var homepath = os.Getenv("HOME") filetodelete := homepath+"/.babil0niafiles/"+filetodel deleteFile(filetodelete) } deleteURL(sqlDB,theintid) } else { fmt.Println("ID dont exist.") } } else { fmt.Println("Usage: bkli delete ") } default: fmt.Println(help) os.Exit(0) } } // Func to add Url. func addUrl(db *sql.DB, url string, description string, tags string) { date := time.Now().Local().String() insertSQL := `INSERT INTO babil0nia(url, description, tags, date) VALUES (?, ?, ?, ?)` statement, err := db.Prepare(insertSQL) if err != nil { log.Fatalln(err.Error()) } _, err = statement.Exec(url, description, tags, date) if err != nil { log.Fatalln(err.Error()) } color.Green("[+] URL added\n") } // Func to modify Url. func modURL(db *sql.DB, url string, description string, tags string, id int) { // Mod url if url != "" { modSQL := `UPDATE babil0nia set url = ? where id = ?` statement, err := db.Prepare(modSQL) if err != nil { log.Fatalln(err.Error()) } _, err = statement.Exec(url,id) if err != nil { log.Fatalln(err.Error()) } } // Mod description if description != "" { modSQL := `UPDATE babil0nia set description = ? where id = ?` statement, err := db.Prepare(modSQL) if err != nil { log.Fatalln(err.Error()) } _, err = statement.Exec(description,id) if err != nil { log.Fatalln(err.Error()) } } // Mod tags if tags != "" { modSQL := `UPDATE babil0nia set tags = ? where id = ?` statement, err := db.Prepare(modSQL) if err != nil { log.Fatalln(err.Error()) } _, err = statement.Exec(tags,id) if err != nil { log.Fatalln(err.Error()) } } //If all ok prints: color.Green("[+] URL modified\n") } // Func to show all URLs. func listUrl(db *sql.DB, description string, tags string) { headerFmt := color.New(color.FgMagenta, color.Underline).SprintfFunc() columnFmt := color.New(color.FgGreen).SprintfFunc() tbl := table.New("ID", "Url", "Description", "Tags","Screenshot") tbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt) row, err := db.Query("SELECT * FROM babil0nia where id like '%' and description like ? and tags like ? ","%"+description+"%","%"+tags+"%") if err != nil { log.Fatal(err) } defer row.Close() fmt.Printf("\n") for row.Next() { var id int var date string var url string var description string var tags string var screenshot string row.Scan(&id, &date, &url, &description, &tags, &screenshot) tbl.AddRow(id,url,description,tags,screenshot) } tbl.Print() fmt.Printf("\n") } // Func to delete Url. func deleteURL(db *sql.DB, id int) { deleteTaskSQL := `DELETE FROM babil0nia WHERE id = ?` statement, err := db.Prepare(deleteTaskSQL) if err != nil { log.Fatalln(err.Error()) } _, err = statement.Exec(id) if err != nil { log.Fatalln(err.Error()) } color.Green("[+] URL %v deleted successfully.\n",id) } // Func to check if ID exists func checkID(db *sql.DB, id int ) int{ var total int row, err := db.Query("SELECT count(*) as total from babil0nia where id = ?",id) if err != nil { log.Fatal(err) } defer row.Close() for row.Next() { var count int row.Scan( &count) total = count } return total } // Func to check if Url is duplicated func checkDupURL(db *sql.DB, url string ) int{ var total int row, err := db.Query("SELECT count(*) as total from babil0nia where url = ?",url) if err != nil { log.Fatal(err) } defer row.Close() for row.Next() { var count int row.Scan( &count) total = count } return total } // Func to check if url has been downloaded. func checkDownload(db *sql.DB, url string ) bool { downloadedfile := false row, err := db.Query("SELECT screenshot from babil0nia where url = ?",url) if err != nil { log.Fatal(err) } defer row.Close() for row.Next() { var screenshot string row.Scan( &screenshot) if screenshot != "" { { downloadedfile = true } } } return downloadedfile } // Func to check if DB file exists, if not, it creates the file. func checkDB(filedb string) { _, err := os.Stat(filedb) //If file htask.db doesnt exist. if err != nil { color.Green("[+]Creating Database\n") file, err := os.Create(filedb) if err != nil { log.Fatal(err.Error()) } file.Close() sqlDB, _ := sql.Open("sqlite3",filedb) defer sqlDB.Close() createTable(sqlDB) } } // Func to create Database file/schema. func createTable(db *sql.DB) { createbabil0niaTableSQL := `CREATE TABLE babil0nia ( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "date" TEXT, "url" TEXT, "description" TEXT, "tags" TEXT, "screenshot" TEXT );` color.Green("[+]Creating babil0nia table...") statement, err := db.Prepare(createbabil0niaTableSQL) if err != nil { log.Fatal(err.Error()) } statement.Exec() color.Green("[+]babil0nia DB created") } // Func to obtain the url given an ID. func obtainURL(db *sql.DB, id int) string{ var result string row, err := db.Query("SELECT url from babil0nia where id = ?",id) if err != nil { log.Fatal(err) } defer row.Close() for row.Next() { var url string row.Scan( &url) result = url } return result } // Func to obtain the download filename given an ID. func obtainDownload(db *sql.DB, id int) string{ var result string row, err := db.Query("SELECT screenshot from babil0nia where id = ?",id) if err != nil { log.Fatal(err) } defer row.Close() for row.Next() { var screenshot string row.Scan( &screenshot) result = screenshot } return result } // Func to create a filename using the md5 hash of the url name. func getFilename(text string) string { hash := md5.Sum([]byte(text)) return hex.EncodeToString(hash[:]) } // Func to take one screenshot from the given URL and save it to a file. It returns error and the name of the saved (screenshot) file. func archiveURL(urltoarchive string, screenshotfullpath string,screenshotname string,db *sql.DB) (error){ var fail error // create context ctx, cancel := chromedp.NewContext( context.Background(), // chromedp.WithDebugf(log.Printf), ) defer cancel() // capture screenshot of an element var buf []byte // capture entire browser viewport, returning png with quality=90 if err := chromedp.Run(ctx, fullScreenshot(urltoarchive, 100, &buf)); err != nil { fail = err } if fail != nil { fmt.Println("Fallo la descarga de la imagen asi que nos e guarda") } else { updateFilename(db,urltoarchive,screenshotname) if err := ioutil.WriteFile(screenshotfullpath, buf, 0o644); err != nil { fail = err } } return fail } func fullScreenshot(urlstr string, quality int, res *[]byte) chromedp.Tasks { return chromedp.Tasks{ chromedp.Navigate(urlstr), chromedp.FullScreenshot(res, quality), } } // Func to add Task. func updateFilename(db *sql.DB, url string, filename string, ) { insertSQL := `UPDATE babil0nia set screenshot = ? where url = ?` statement, err := db.Prepare(insertSQL) if err != nil { log.Fatalln(err.Error()) } _, err = statement.Exec(filename, url) if err != nil { log.Fatalln(err.Error()) } color.Green("[+] Download updated.\n") } func deleteFile(filetodelete string){ e := os.Remove(filetodelete) if e != nil { fmt.Printf("File %v was not found",filetodelete) } }