sprechstunden-go/repositories/request.go

147 lines
4.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// request
package repositories
import (
"bytes"
"crypto/rand"
"database/sql"
"html/template"
"math/big"
"net/smtp"
"sprechstundentool/models"
)
type RequestRepo struct {
db *sql.DB
officeHourRepo models.OfficeHourRepository
}
func NewRequestRepo(db *sql.DB, officeHourRepo models.OfficeHourRepository) *RequestRepo {
return &RequestRepo{
db: db,
officeHourRepo: officeHourRepo,
}
}
func (r *RequestRepo) FindBySecret(secret string) (models.Request, error) {
// This query is not safe against timing sidechannel attacks I don't care.
row := r.db.QueryRow("SELECT * FROM request WHERE secret=?", secret)
var request models.Request
var officeHourId int
err := row.Scan(&request.Id, &officeHourId, &request.Action, &request.Secret)
if err != nil {
return models.Request{}, err
}
request.OfficeHour, err = r.officeHourRepo.FindById(officeHourId)
return request, err
}
func (r *RequestRepo) FindByOfficeHour(officeHour models.OfficeHour) ([]models.Request, error) {
rows, err := r.db.Query("SELECT * FROM request WHERE officeHour=?", officeHour.Id)
if err != nil {
return nil, err
}
defer rows.Close()
var requests []models.Request
for rows.Next() {
var request models.Request
var officeHourId int
if err := rows.Scan(&request.Id, &officeHourId, &request.Action, &request.Secret); err != nil {
return requests, err
}
request.OfficeHour, err = r.officeHourRepo.FindById(officeHourId)
if err != nil {
return requests, err
}
requests = append(requests, request)
}
return requests, nil
}
func (r *RequestRepo) Add(officeHour models.OfficeHour, action int) (int, error) {
existents, err := r.FindByOfficeHour(officeHour)
if err != nil && err != sql.ErrNoRows {
return 0, err
}
/* Resend confirmation mail if identical request exists,
* but don't insert new request into database.
*/
for _, request := range existents {
if request.Action == action { // already covered by selection && request.OfficeHour == officeHour {
return request.Id, sendConfirmationMail(request)
}
}
secret, err := r.newSecret()
if err != nil {
return 0, err
}
request := models.Request{0, officeHour, action, secret}
_, err = r.db.Exec("INSERT INTO `request` (officeHour, action, secret) VALUES (?,?,?)", officeHour.Id, action, secret)
if err != nil {
return 0, err
}
request, err = r.FindBySecret(secret)
if err != nil {
return request.Id, err
}
return request.Id, sendConfirmationMail(request)
}
func (r *RequestRepo) Execute(request models.Request) error {
var err error
switch request.Action {
case models.RequestActivate:
_, err = r.db.Exec("UPDATE officeHour SET active=true WHERE id=?", request.OfficeHour.Id)
r.db.Exec("DELETE FROM request WHERE officeHour=?", request.OfficeHour.Id)
case models.RequestDelete:
r.officeHourRepo.Delete(request.OfficeHour)
r.db.Exec("DELETE FROM request WHERE officeHour=?", request.OfficeHour.Id)
default:
r.db.Exec("DELETE FROM request WHERE id=?", request.Id)
}
return err
}
func (r *RequestRepo) newSecret() (string, error) {
secret := randomString(models.SecretLength)
_, err := r.FindBySecret(secret)
if err != nil && err != sql.ErrNoRows {
return "", err
}
// find unused secret
for err != sql.ErrNoRows {
secret = randomString(models.SecretLength)
_, err = r.FindBySecret(secret)
}
return secret, nil
}
func sendConfirmationMail(request models.Request) error {
to := []string{request.OfficeHour.Tutor.Email}
tmpl, err := template.New("confirmationMail").Funcs(template.FuncMap{"DayName": models.DayName}).ParseFiles("templates/confirmationMail")
var message bytes.Buffer
err = tmpl.Execute(&message, request)
if err != nil {
return err
}
err = smtp.SendMail("192.168.0.24:25", nil, "Mathebau Sprechstunden <sprechstunden@mathebau.de>", to, message.Bytes())
return err
}
func randomString(n int) string {
// []byte would be faster and also work, but []rune keeps working for UTF8 characters
// if someone would like them.
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
s := make([]rune, n)
for i := range s {
position, _ := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
s[i] = letters[position.Int64()]
}
return string(s)
}