commit cb30c46aa50f8f9c3661480467df4067abdc0a56
parent a5bd60f719958c77bb7f9338c926574ce9f1a1fc
Author: Andreas Gruhler <andreas.gruhler@adfinis.com>
Date: Wed, 4 Oct 2023 23:16:43 +0200
feat: success msg and ics folding
Diffstat:
M | server.go | | | 97 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- |
1 file changed, 87 insertions(+), 10 deletions(-)
diff --git a/server.go b/server.go
@@ -15,6 +15,7 @@ import (
"hivedav/caldav"
"hivedav/config"
"hivedav/tzdb"
+ "html"
"html/template"
"io"
"log"
@@ -43,9 +44,9 @@ type TableData struct {
}
type CubicleFormData struct {
- Dtstart string
+ Dtstart string
DtstartInput string
- Version string
+ Version string
}
type IcsData struct {
@@ -631,8 +632,8 @@ func (s *Server) CubicleForm(w http.ResponseWriter, req *http.Request, ps httpro
icsData := CubicleFormData{
DtstartInput: dtStartTimeLocal.Format("2006-01-02T15:04"),
- Dtstart: dtstart,
- Version: hivedavVersion,
+ Dtstart: dtstart,
+ Version: hivedavVersion,
}
w.Header().Set("Content-Type", "text/html")
@@ -674,8 +675,7 @@ func (s *Server) BookCubicle(w http.ResponseWriter, req *http.Request, ps httpro
}
mail := req.FormValue("mail")
- msg := req.FormValue("msg")
- log.Printf("Form: %+v\n", req.Form)
+ msg := html.UnescapeString(req.FormValue("msg"))
if mail == "" || msg == "" {
http.Error(w, fmt.Sprintf("Mail and msg required, try again: 'curl %s/book/%s -F 'mail=' -F 'msg='\n", s.config.HiveDavHost, dtstart), http.StatusBadRequest)
@@ -688,7 +688,9 @@ func (s *Server) BookCubicle(w http.ResponseWriter, req *http.Request, ps httpro
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
- // TODO: print success message to user
+
+ zone, _ := dtStartTimeLocal.Zone()
+ io.WriteString(w, fmt.Sprintf("Thank you for booking on %s %s\n", dtStartTimeLocal.Format("02 Jan 2006 15:04"), zone))
return
}
@@ -767,13 +769,13 @@ func (s *Server) sendMail(recepient string, msg string, dtstart time.Time) error
DstTzOffsetTo: dstTzOffsetTo,
Summary: s.config.BookingSummary,
Reminder: s.config.BookingReminder,
- Description: msg,
+ Description: fold(msg),
Location: fmt.Sprintf("%s/%s", s.config.BookingLocation, strings.ToUpper(uuid)),
Organizer: s.config.CaldavUser,
Attendee: recepient,
}
- log.Printf("IcsData: +%v\n", icsData)
+ log.Printf("DESCRIPTION: %v\n", icsData.Description)
err = icsTmpl.Execute(icsFile, icsData)
if err != nil {
@@ -783,7 +785,6 @@ func (s *Server) sendMail(recepient string, msg string, dtstart time.Time) error
e.AttachFile(tmpFileName)
// https://datatracker.ietf.org/doc/html/rfc2447#section-2.4
e.Attachments[0].ContentType = "text/calendar; charset=utf-8; method=REQUEST"
- log.Printf("%s\n", e.Attachments[0].Content)
smtpServer := fmt.Sprintf("%s:%d", s.config.SmtpHost, s.config.SmtpPort)
auth := smtp.PlainAuth("", s.config.SmtpUser, s.config.SmtpPassword, s.config.SmtpHost)
@@ -800,6 +801,82 @@ func (s *Server) sendMail(recepient string, msg string, dtstart time.Time) error
return nil
}
+// simple string to rune and hex conversion in Python
+// i=ord("\0"); hex(i)
+func fold(s string) string {
+ // convert the string unicode code points
+ runeStr := []rune(s)
+
+ // create buffer for escaped result TEXT
+ buf := make([]rune, 1)
+
+ // bufl is the current buffer size incl. \0
+ var bufl = 1
+ // i is the iterator in s
+ var i = 0
+
+ // escch is the char to be escaped,
+ // only written when esc=true
+ escch := rune(0x0) // \0
+ esc := false
+
+ for i < len(runeStr)-1 || esc {
+ buf = append(buf, rune(0x0))
+ bufl++
+
+ if (bufl > 1) && ((bufl % 77) == 0) {
+ // break lines after 75 chars
+ // split between any two characters by inserting a CRLF
+ // immediately followed by a white space character
+ buf[bufl-2] = rune(0xa) // newline '\n'
+ escch = rune(0x20) // whitespace ' '
+ esc = true
+ continue
+ }
+
+ if esc {
+ // only escape char, do not advance iterator i
+ buf[bufl-2] = escch
+ esc = false
+ } else {
+ // escape characters
+ // https://datatracker.ietf.org/doc/html/rfc5545#section-3.3.11
+ switch runeStr[i] {
+ case rune(0x5c): // backslash `\`
+ buf[bufl-2] = rune(0x5c)
+ escch = rune(0x5c)
+ esc = true
+ break
+ case rune(0x3b): // semicolon ';'
+ buf[bufl-2] = rune(0x5c)
+ escch = rune(0x3b)
+ esc = true
+ break
+ case rune(0x2c): // comma ','
+ buf[bufl-2] = rune(0x5c)
+ escch = rune(0x2c)
+ esc = true
+ break
+ case rune(0xa): // newline '\n'
+ buf[bufl-2] = rune(0x5c) // backslash '\'
+ escch = rune(0x6e) // literal 'n'
+ esc = true
+ break
+ default:
+ // write regular character from runeStr
+ buf[bufl-2] = runeStr[i]
+ break
+ }
+ i++
+ }
+
+ // terminate the char string in any case (esc or not)
+ buf[bufl-1] = rune(0x0)
+ }
+
+ return string(buf)
+}
+
// https://datatracker.ietf.org/doc/html/rfc7986#section-5.3
func genUid(n int) (string, error) {
bytes := make([]byte, n)