355 lines
6.5 KiB
Go
355 lines
6.5 KiB
Go
/**
|
|
* @file storage.go
|
|
* @author Mikhail Klementyev jollheef<AT>riseup.net
|
|
* @license GNU GPLv3
|
|
* @date July, 2016
|
|
* @brief Database functions
|
|
*/
|
|
|
|
package storage
|
|
|
|
import (
|
|
"bytes"
|
|
"database/sql"
|
|
"encoding/base64"
|
|
"encoding/gob"
|
|
"errors"
|
|
"log"
|
|
"net/http"
|
|
"reflect"
|
|
"strings"
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
|
)
|
|
|
|
func toGOB64(m http.Cookie) string {
|
|
b := bytes.Buffer{}
|
|
e := gob.NewEncoder(&b)
|
|
err := e.Encode(m)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return base64.StdEncoding.EncodeToString(b.Bytes())
|
|
}
|
|
|
|
func fromGOB64(str string) http.Cookie {
|
|
m := http.Cookie{}
|
|
by, err := base64.StdEncoding.DecodeString(str)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
b := bytes.Buffer{}
|
|
b.Write(by)
|
|
d := gob.NewDecoder(&b)
|
|
err = d.Decode(&m)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return m
|
|
}
|
|
|
|
func serializeCookies(cookies []*http.Cookie) (s string) {
|
|
for _, c := range cookies {
|
|
s += toGOB64(*c) + " "
|
|
}
|
|
return
|
|
}
|
|
|
|
func deserializeCookies(s string) (cookies []*http.Cookie) {
|
|
gob64Objects := strings.Split(s, " ")
|
|
for _, g := range gob64Objects {
|
|
c := fromGOB64(g)
|
|
cookies = append(cookies, &c)
|
|
}
|
|
return
|
|
}
|
|
|
|
func OpenDB(path string) (db *sql.DB, err error) {
|
|
db, err = sql.Open("sqlite3", path)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
_, err = db.Exec("CREATE TABLE IF NOT EXISTS `links` " +
|
|
"( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `url` TEXT );")
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
_, err = db.Exec("CREATE TABLE IF NOT EXISTS `history` " +
|
|
"( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `url` TEXT );")
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
_, err = db.Exec("CREATE TABLE IF NOT EXISTS `fields` " +
|
|
"( `id` INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
|
" `form_id` INTEGER, " +
|
|
" `hidden` BOOLEAN, " +
|
|
" `value` TEXT, " +
|
|
" `name` TEXT );")
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
_, err = db.Exec("CREATE TABLE IF NOT EXISTS `forms` " +
|
|
"( `id` INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
|
" `post` BOOLEAN, " +
|
|
" `url` TEXT );")
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
_, err = db.Exec("CREATE TABLE IF NOT EXISTS `cookies` " +
|
|
"( `id` INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
|
" `url` BOOLEAN, " +
|
|
" `cookies` TEXT );")
|
|
|
|
return
|
|
}
|
|
|
|
func AddCookies(db *sql.DB, url string, cookies []*http.Cookie) (err error) {
|
|
log.Println("Add cookies", url, cookies)
|
|
stmt, err := db.Prepare("INSERT INTO `cookies` " +
|
|
"(`url`, `cookies`) VALUES ($1, $2);")
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer stmt.Close()
|
|
|
|
_, err = stmt.Exec(url, serializeCookies(cookies))
|
|
return
|
|
}
|
|
|
|
func GetCookies(db *sql.DB, url string) (cookies []*http.Cookie, err error) {
|
|
stmt, err := db.Prepare("SELECT `cookies` FROM `cookies` WHERE url=$1;")
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer stmt.Close()
|
|
|
|
var rawCookies string
|
|
err = stmt.QueryRow(url).Scan(&rawCookies)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
cookies = deserializeCookies(rawCookies)
|
|
|
|
return
|
|
}
|
|
|
|
type Field struct {
|
|
Hidden bool
|
|
Value string
|
|
Name string
|
|
}
|
|
|
|
func getFields(db *sql.DB, formNo int64) (fields []Field, err error) {
|
|
stmt, err := db.Prepare("SELECT `hidden`, `value`, `name` FROM `fields` WHERE form_id=$1;")
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer stmt.Close()
|
|
|
|
rows, err := stmt.Query(formNo)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
var f Field
|
|
err = rows.Scan(&f.Hidden, &f.Value, &f.Name)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
fields = append(fields, f)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func addField(db *sql.DB, name, value string, hidden bool, formNo int64) (err error) {
|
|
stmt, err := db.Prepare("INSERT INTO `fields` " +
|
|
"(`form_id`, `name`, `hidden`, `value`) VALUES ($1, $2, $3, $4);")
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer stmt.Close()
|
|
|
|
_, err = stmt.Exec(formNo, name, hidden, value)
|
|
return
|
|
}
|
|
|
|
func AddForm(db *sql.DB, fields []Field, url, method string) (formNo int64, err error) {
|
|
stmt, err := db.Prepare("INSERT INTO `forms` (`url`, `post`) VALUES ($1, $2);")
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer stmt.Close()
|
|
|
|
post := false // GET
|
|
if strings.ToUpper(method) == "POST" {
|
|
post = true
|
|
}
|
|
|
|
r, err := stmt.Exec(url, post)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
formNo, err = r.LastInsertId()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for _, f := range fields {
|
|
addField(db, f.Name, f.Value, f.Hidden, formNo)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func GetFormID(db *sql.DB, fields []Field, url, method string) (formNo int64, err error) {
|
|
stmt, err := db.Prepare("SELECT `id` FROM `forms` WHERE url=$1 AND post=$2;")
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer stmt.Close()
|
|
|
|
post := false // GET
|
|
if strings.ToUpper(method) == "POST" {
|
|
post = true
|
|
}
|
|
|
|
err = stmt.QueryRow(url, post).Scan(&formNo)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
dbFields, err := getFields(db, formNo)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if !reflect.DeepEqual(fields, dbFields) {
|
|
err = errors.New("Fields not match")
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func GetForm(db *sql.DB, formID int64) (fields []Field, url string, post bool, err error) {
|
|
stmt, err := db.Prepare("SELECT `post`, `url` FROM `forms` WHERE id=$1;")
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer stmt.Close()
|
|
|
|
err = stmt.QueryRow(formID).Scan(&post, &url)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
fields, err = getFields(db, formID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func AddLink(db *sql.DB, url string) (linkNo int64, err error) {
|
|
stmt, err := db.Prepare("INSERT INTO `links` (`url`) VALUES ($1);")
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer stmt.Close()
|
|
|
|
r, err := stmt.Exec(url)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
linkNo, err = r.LastInsertId()
|
|
|
|
return
|
|
}
|
|
|
|
func GetLink(db *sql.DB, linkID int64) (url string, err error) {
|
|
stmt, err := db.Prepare("SELECT `url` FROM `links` WHERE id=$1;")
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer stmt.Close()
|
|
|
|
err = stmt.QueryRow(linkID).Scan(&url)
|
|
|
|
return
|
|
}
|
|
|
|
func GetLinkID(db *sql.DB, url string) (linkID int64, err error) {
|
|
stmt, err := db.Prepare("SELECT `id` FROM `links` WHERE url=$1;")
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer stmt.Close()
|
|
|
|
err = stmt.QueryRow(url).Scan(&linkID)
|
|
|
|
return
|
|
}
|
|
|
|
func AddHistoryURL(db *sql.DB, url string) (err error) {
|
|
stmt, err := db.Prepare("INSERT INTO `history` (`url`) VALUES ($1);")
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer stmt.Close()
|
|
|
|
_, err = stmt.Exec(url)
|
|
|
|
return
|
|
}
|
|
|
|
type HistoryItem struct {
|
|
ID int64
|
|
URL string
|
|
}
|
|
|
|
func GetHistory(db *sql.DB) (history []HistoryItem, err error) {
|
|
rows, err := db.Query("SELECT `id`, `url` FROM `history`;")
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
var h HistoryItem
|
|
|
|
err = rows.Scan(&h.ID, &h.URL)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
history = append(history, h)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func GetHistoryUrl(db *sql.DB, historyID int64) (url string, err error) {
|
|
stmt, err := db.Prepare("SELECT `url` FROM `history` WHERE id=$1;")
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer stmt.Close()
|
|
|
|
err = stmt.QueryRow(historyID).Scan(&url)
|
|
|
|
return
|
|
}
|