all repos — honk @ 85aa13c31a3299ecf4d046b2a0475ba5176057f5

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 savedstyleparam string
 53
 54func getstyleparam() string {
 55	if savedstyleparam != "" {
 56		return savedstyleparam
 57	}
 58	data, _ := ioutil.ReadFile("views/style.css")
 59	hasher := sha512.New()
 60	hasher.Write(data)
 61	return fmt.Sprintf("?v=%.8x", hasher.Sum(nil))
 62}
 63
 64var dbtimeformat = "2006-01-02 15:04:05"
 65
 66var alreadyopendb *sql.DB
 67var dbname = "honk.db"
 68var stmtConfig *sql.Stmt
 69
 70func initdb() {
 71	schema, err := ioutil.ReadFile("schema.sql")
 72	if err != nil {
 73		log.Fatal(err)
 74	}
 75	_, err = os.Stat(dbname)
 76	if err == nil {
 77		log.Fatalf("%s already exists", dbname)
 78	}
 79	db, err := sql.Open("sqlite3", dbname)
 80	if err != nil {
 81		log.Fatal(err)
 82	}
 83	defer func() {
 84		os.Remove(dbname)
 85		os.Exit(1)
 86	}()
 87	c := make(chan os.Signal)
 88	signal.Notify(c, os.Interrupt)
 89	go func() {
 90		<-c
 91		C.termecho(1)
 92		fmt.Printf("\n")
 93		os.Remove(dbname)
 94		os.Exit(1)
 95	}()
 96
 97	for _, line := range strings.Split(string(schema), ";") {
 98		_, err = db.Exec(line)
 99		if err != nil {
100			log.Print(err)
101			return
102		}
103	}
104	defer db.Close()
105	r := bufio.NewReader(os.Stdin)
106	fmt.Printf("username: ")
107	name, err := r.ReadString('\n')
108	if err != nil {
109		log.Print(err)
110		return
111	}
112	name = name[:len(name)-1]
113	if len(name) < 1 {
114		log.Print("that's way too short")
115		return
116	}
117	C.termecho(0)
118	fmt.Printf("password: ")
119	pass, err := r.ReadString('\n')
120	C.termecho(1)
121	fmt.Printf("\n")
122	if err != nil {
123		log.Print(err)
124		return
125	}
126	pass = pass[:len(pass)-1]
127	if len(pass) < 6 {
128		log.Print("that's way too short")
129		return
130	}
131	hash, err := bcrypt.GenerateFromPassword([]byte(pass), 12)
132	if err != nil {
133		log.Print(err)
134		return
135	}
136	_, err = db.Exec("insert into users (username, hash) values (?, ?)", name, hash)
137	if err != nil {
138		log.Print(err)
139		return
140	}
141	fmt.Printf("listen address: ")
142	addr, err := r.ReadString('\n')
143	if err != nil {
144		log.Print(err)
145		return
146	}
147	addr = addr[:len(addr)-1]
148	if len(addr) < 1 {
149		log.Print("that's way too short")
150		return
151	}
152	_, err = db.Exec("insert into config (key, value) values (?, ?)", "listenaddr", addr)
153	if err != nil {
154		log.Print(err)
155		return
156	}
157	fmt.Printf("server name: ")
158	addr, err = r.ReadString('\n')
159	if err != nil {
160		log.Print(err)
161		return
162	}
163	addr = addr[:len(addr)-1]
164	if len(addr) < 1 {
165		log.Print("that's way too short")
166		return
167	}
168	_, err = db.Exec("insert into config (key, value) values (?, ?)", "servername", addr)
169	if err != nil {
170		log.Print(err)
171		return
172	}
173	var randbytes [16]byte
174	rand.Read(randbytes[:])
175	key := fmt.Sprintf("%x", randbytes)
176	_, err = db.Exec("insert into config (key, value) values (?, ?)", "csrfkey", key)
177	if err != nil {
178		log.Print(err)
179		return
180	}
181	err = finishusersetup()
182	if err != nil {
183		log.Print(err)
184		return
185	}
186	prepareStatements(db)
187	db.Close()
188	fmt.Printf("done.\n")
189	os.Exit(0)
190}
191
192func opendatabase() *sql.DB {
193	if alreadyopendb != nil {
194		return alreadyopendb
195	}
196	var err error
197	_, err = os.Stat(dbname)
198	if err != nil {
199		log.Fatalf("unable to open database: %s", err)
200	}
201	db, err := sql.Open("sqlite3", dbname)
202	if err != nil {
203		log.Fatalf("unable to open database: %s", err)
204	}
205	stmtConfig, err = db.Prepare("select value from config where key = ?")
206	if err != nil {
207		log.Fatal(err)
208	}
209	alreadyopendb = db
210	return db
211}
212
213func getconfig(key string, value interface{}) error {
214	row := stmtConfig.QueryRow(key)
215	err := row.Scan(value)
216	if err == sql.ErrNoRows {
217		err = nil
218	}
219	return err
220}
221
222func openListener() (net.Listener, error) {
223	var listenAddr string
224	err := getconfig("listenaddr", &listenAddr)
225	if err != nil {
226		return nil, err
227	}
228	if listenAddr == "" {
229		return nil, fmt.Errorf("must have listenaddr")
230	}
231	proto := "tcp"
232	if listenAddr[0] == '/' {
233		proto = "unix"
234		err := os.Remove(listenAddr)
235		if err != nil && !os.IsNotExist(err) {
236			log.Printf("unable to unlink socket: %s", err)
237		}
238	}
239	listener, err := net.Listen(proto, listenAddr)
240	if err != nil {
241		return nil, err
242	}
243	if proto == "unix" {
244		os.Chmod(listenAddr, 0777)
245	}
246	return listener, nil
247}