all repos — honk @ f9fb2c73760045569f460dac473454ccfb4ea049

my fork of honk

util.go (view raw)

  1//
  2// Copyright (c) 2019 Ted Unangst <tedu@tedunangst.com>
  3//
  4// Permission to use, copy, modify, and distribute this software for any
  5// purpose with or without fee is hereby granted, provided that the above
  6// copyright notice and this permission notice appear in all copies.
  7//
  8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 11// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 12// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 15
 16package main
 17
 18/*
 19#include <termios.h>
 20
 21void
 22termecho(int on)
 23{
 24	struct termios t;
 25	tcgetattr(1, &t);
 26	if (on)
 27		t.c_lflag |= ECHO;
 28	else
 29		t.c_lflag &= ~ECHO;
 30	tcsetattr(1, TCSADRAIN, &t);
 31}
 32*/
 33import "C"
 34
 35import (
 36	"bufio"
 37	"crypto/rand"
 38	"crypto/sha512"
 39	"database/sql"
 40	"fmt"
 41	"io/ioutil"
 42	"log"
 43	"net"
 44	"os"
 45	"os/signal"
 46	"strings"
 47
 48	"golang.org/x/crypto/bcrypt"
 49	_ "humungus.tedunangst.com/r/go-sqlite3"
 50)
 51
 52var savedstyleparams = make(map[string]string)
 53
 54func getstyleparam(file string) string {
 55	if p, ok := savedstyleparams[file]; ok {
 56		return p
 57	}
 58	data, err := ioutil.ReadFile(file)
 59	if err != nil {
 60		return ""
 61	}
 62	hasher := sha512.New()
 63	hasher.Write(data)
 64
 65	return fmt.Sprintf("?v=%.8x", hasher.Sum(nil))
 66}
 67
 68var dbtimeformat = "2006-01-02 15:04:05"
 69
 70var alreadyopendb *sql.DB
 71var dbname = "honk.db"
 72var stmtConfig *sql.Stmt
 73var myVersion = 8
 74
 75func initdb() {
 76	schema, err := ioutil.ReadFile("schema.sql")
 77	if err != nil {
 78		log.Fatal(err)
 79	}
 80	_, err = os.Stat(dbname)
 81	if err == nil {
 82		log.Fatalf("%s already exists", dbname)
 83	}
 84	db, err := sql.Open("sqlite3", dbname)
 85	if err != nil {
 86		log.Fatal(err)
 87	}
 88	defer func() {
 89		os.Remove(dbname)
 90		os.Exit(1)
 91	}()
 92	c := make(chan os.Signal)
 93	signal.Notify(c, os.Interrupt)
 94	go func() {
 95		<-c
 96		C.termecho(1)
 97		fmt.Printf("\n")
 98		os.Remove(dbname)
 99		os.Exit(1)
100	}()
101
102	for _, line := range strings.Split(string(schema), ";") {
103		_, err = db.Exec(line)
104		if err != nil {
105			log.Print(err)
106			return
107		}
108	}
109	defer db.Close()
110	r := bufio.NewReader(os.Stdin)
111	fmt.Printf("username: ")
112	name, err := r.ReadString('\n')
113	if err != nil {
114		log.Print(err)
115		return
116	}
117	name = name[:len(name)-1]
118	if len(name) < 1 {
119		log.Print("that's way too short")
120		return
121	}
122	C.termecho(0)
123	fmt.Printf("password: ")
124	pass, err := r.ReadString('\n')
125	C.termecho(1)
126	fmt.Printf("\n")
127	if err != nil {
128		log.Print(err)
129		return
130	}
131	pass = pass[:len(pass)-1]
132	if len(pass) < 6 {
133		log.Print("that's way too short")
134		return
135	}
136	hash, err := bcrypt.GenerateFromPassword([]byte(pass), 12)
137	if err != nil {
138		log.Print(err)
139		return
140	}
141	_, err = db.Exec("insert into users (username, hash) values (?, ?)", name, hash)
142	if err != nil {
143		log.Print(err)
144		return
145	}
146	fmt.Printf("listen address: ")
147	addr, err := r.ReadString('\n')
148	if err != nil {
149		log.Print(err)
150		return
151	}
152	addr = addr[:len(addr)-1]
153	if len(addr) < 1 {
154		log.Print("that's way too short")
155		return
156	}
157	_, err = db.Exec("insert into config (key, value) values (?, ?)", "listenaddr", addr)
158	if err != nil {
159		log.Print(err)
160		return
161	}
162	fmt.Printf("server name: ")
163	addr, err = r.ReadString('\n')
164	if err != nil {
165		log.Print(err)
166		return
167	}
168	addr = addr[:len(addr)-1]
169	if len(addr) < 1 {
170		log.Print("that's way too short")
171		return
172	}
173	_, err = db.Exec("insert into config (key, value) values (?, ?)", "servername", addr)
174	if err != nil {
175		log.Print(err)
176		return
177	}
178	var randbytes [16]byte
179	rand.Read(randbytes[:])
180	key := fmt.Sprintf("%x", randbytes)
181	_, err = db.Exec("insert into config (key, value) values (?, ?)", "csrfkey", key)
182	if err != nil {
183		log.Print(err)
184		return
185	}
186	_, err = db.Exec("insert into config (key, value) values (?, ?)", "dbversion", myVersion)
187	if err != nil {
188		log.Print(err)
189		return
190	}
191	err = finishusersetup()
192	if err != nil {
193		log.Print(err)
194		return
195	}
196	prepareStatements(db)
197	db.Close()
198	fmt.Printf("done.\n")
199	os.Exit(0)
200}
201
202func opendatabase() *sql.DB {
203	if alreadyopendb != nil {
204		return alreadyopendb
205	}
206	var err error
207	_, err = os.Stat(dbname)
208	if err != nil {
209		log.Fatalf("unable to open database: %s", err)
210	}
211	db, err := sql.Open("sqlite3", dbname)
212	if err != nil {
213		log.Fatalf("unable to open database: %s", err)
214	}
215	stmtConfig, err = db.Prepare("select value from config where key = ?")
216	if err != nil {
217		log.Fatal(err)
218	}
219	alreadyopendb = db
220	return db
221}
222
223func getconfig(key string, value interface{}) error {
224	row := stmtConfig.QueryRow(key)
225	err := row.Scan(value)
226	if err == sql.ErrNoRows {
227		err = nil
228	}
229	return err
230}
231
232func openListener() (net.Listener, error) {
233	var listenAddr string
234	err := getconfig("listenaddr", &listenAddr)
235	if err != nil {
236		return nil, err
237	}
238	if listenAddr == "" {
239		return nil, fmt.Errorf("must have listenaddr")
240	}
241	proto := "tcp"
242	if listenAddr[0] == '/' {
243		proto = "unix"
244		err := os.Remove(listenAddr)
245		if err != nil && !os.IsNotExist(err) {
246			log.Printf("unable to unlink socket: %s", err)
247		}
248	}
249	listener, err := net.Listen(proto, listenAddr)
250	if err != nil {
251		return nil, err
252	}
253	if proto == "unix" {
254		os.Chmod(listenAddr, 0777)
255	}
256	return listener, nil
257}