hlfw.ca

webbing

Download patch

ref: c5b4df0722a91479d71b6a96e6bfca02306efc9c
parent: 07b5f49b34d8b32887b18ff79b47cb863756da1d
author: halfwit <michaelmisch1985@gmail.com>
date: Thu Aug 6 05:26:17 PDT 2020

Add plugins

--- a/appointments.go
+++ /dev/null
@@ -1,25 +1,0 @@
-package plugins
-
-import (
-	"github.com/olmaxmedical/router"
-)
-
-// AddAppointment registers an appointment into the appointment book
-// TODO(halfwit) message/email client to fill out Symptoms form
-const AddAppointment router.PluginMask = 1 << 1
-
-func init() {
-	b := &router.Plugin{
-		Name:     "Add Appointments",
-		Run:      nil,
-		Validate: addAppt,
-	}
-	router.AddPlugin(b, AddAppointment)
-}
-
-func addAppt(s *router.Request) error {
-	// get &id=
-	// Auth user and requisite key
-	// TODO(halfwit) create unique key in patient/appointments 
-	return nil
-}
--- a/bookings.go
+++ /dev/null
@@ -1,22 +1,0 @@
-package plugins
-
-import (
-	"github.com/olmaxmedical/router"
-)
-
-// ListBookings retreives a list of all upcoming bookings for a given doctor
-const ListBookings router.PluginMask = 1 << 2
-
-func init() {
-	b := &router.Plugin{
-		Name:     "List Bookings",
-		Run:      nil,
-		Validate: listBookings,
-	}
-	router.AddPlugin(b, ListBookings)
-}
-
-func listBookings(s *router.Request) error {
-	
-	return nil
-}
--- a/countries.go
+++ /dev/null
@@ -1,96 +1,0 @@
-package plugins
-
-import (
-	"fmt"
-	"sort"
-	"strings"
-
-	"github.com/olmaxmedical/router"
-	"github.com/pariz/gountries"
-	"golang.org/x/text/message"
-)
-
-// Countries - Populate a localized spinner to select country
-const Countries router.PluginMask = 1 << 3
-
-// Country - Mapping token to internationalized country code
-type Country struct {
-	ID   string
-	Name string
-}
-
-type countries struct {
-	list []gountries.Country
-}
-
-var cache *countries
-
-func init() {
-	var l []gountries.Country
-	c := gountries.New()
-	for _, c := range c.FindAllCountries() {
-		l = append(l, c)
-	}
-	cache = &countries{
-		list: l,
-	}
-	sort.Sort(cache)
-	b := &router.Plugin{
-		Name:     "country",
-		Run:      listCountries,
-		Validate: validateCountries,
-	}
-	router.AddPlugin(b, Countries)
-}
-
-// Len - For Sort implementation
-func (c *countries) Len() int {
-	return len(c.list)
-}
-
-// Less - For Sort implementation
-func (c *countries) Less(i, j int) bool {
-	switch strings.Compare(c.list[i].Name.Common, c.list[j].Name.Common) {
-	case -1:
-		return true
-	default:
-		return false
-	}
-}
-
-// Swap - For Sort implementation
-func (c *countries) Swap(i, j int) {
-	tmp := c.list[i]
-	c.list[i] = c.list[j]
-	c.list[j] = tmp
-}
-
-func listCountries(r *router.Request) map[string]interface{} {
-	p := r.Printer()
-	// TODO(halfwit): Use Request to get a localized country name
-	c := make(map[string]interface{})
-	c["label"] = p.Sprint("Select country/countries")
-	for _, item := range cache.list {
-		c[item.Name.Common] = item.Name.Common
-	}
-	return c
-}
-
-func validateCountries(r *router.Request) error {
-	s := r.Request()
-	for _, c := range s.PostFormValue("country") {
-		if msg := checkCountry(r.Printer(), c); msg != nil {
-			return msg
-		}
-	}
-	return nil
-}
-
-func checkCountry(p *message.Printer, country rune) error {
-	for _, item := range cache.list {
-		if item.Name.Common == string(country) {
-			return nil
-		}
-	}
-	return fmt.Errorf("%s", p.Sprint("No country entered/nil value entered"))
-}
--- a/doctor.go
+++ /dev/null
@@ -1,55 +1,0 @@
-package plugins
-
-import (
-	"github.com/olmaxmedical/router"
-)
-
-// ListDoctors - Bitmask to list doctors of in client country
-const ListDoctors router.PluginMask = 1 << 4
-
-type doctor struct {
-	Image     string
-	AlmaMater string
-	Name      string
-	Residency string
-	Current   string
-	Country   string
-	Specialty string
-	Rate      string
-}
-
-func init() {
-	b := &router.Plugin{
-		Name:     "doctors",
-		Run:      ListDocs,
-		Validate: nil,
-	}
-	router.AddPlugin(b, ListDoctors)
-}
-
-// ListDocs - Query db and return list of doctors in country
-// These may need eventual localization for any bilingual doctors we have
-func ListDocs(r *router.Request) map[string]interface{} {
-	return map[string]interface{}{
-		"Mark Abuzamzam, MD": &doctor{
-			Image:     "AbuzamzamMD.jpg",
-			AlmaMater: "University of Southern California School of Medicine",
-			Residency: "University of Southern California, San Diego. Internal Medicine Residency",
-			Name:      "Mark Abuzamzam, MD",
-			Current:   "Current Faculty at University of California Irvine Program Director",
-			Country:   "United States of America",
-			Specialty: "Internal Medicine and Addictions Medicine",
-			Rate:      "0.0013 BTC",
-		},
-		"Martha Woodfin, MD": &doctor{
-			Image:     "WoodfinMD.jpg",
-			Name:      "Martha Woodfin, MD",
-			AlmaMater: "University Seoul School of Medicine",
-			Residency: "University of Las Vegas Nevada, Pediatric Medicine Residency",
-			Current:   "Current Staff at Mercy Hospital Jackson NC",
-			Country:   "United States of America",
-			Specialty: "Internal Medicine",
-			Rate:      "0.0011 BTC",
-		},
-	}
-}
--- a/email.go
+++ /dev/null
@@ -1,83 +1,0 @@
-package plugins
-
-import (
-	"errors"
-	"log"
-	"mime/multipart"
-
-	"github.com/olmaxmedical/email"
-	"github.com/olmaxmedical/router"
-)
-
-// EmailForm - Patient form to gmail
-const EmailForm router.PluginMask = 1 << 5
-
-// SendSignup - Send account creation validation email
-const SendSignup router.PluginMask = 1 << 6
-
-// SendReset - Send password reset email
-const SendReset router.PluginMask = 1 << 7
-
-func init() {
-	b := &router.Plugin{
-		Name:     "emailform",
-		Run:      nil,
-		Validate: emailForm,
-	}
-	router.AddPlugin(b, EmailForm)
-	c := &router.Plugin{
-		Name:     "signupEmail",
-		Run:      nil,
-		Validate: signupEmail,
-	}
-	router.AddPlugin(c, SendSignup)
-	d := &router.Plugin{
-		Name:     "resetPassword",
-		Run:      nil,
-		Validate: resetPassword,
-	}
-	router.AddPlugin(d, SendReset)
-}
-
-func signupEmail(s *router.Request) error {
-	r := s.Request()
-	first := r.PostFormValue("fname")
-	last := r.PostFormValue("lname")
-	address := r.PostFormValue("email")
-	pass := r.PostFormValue("pass")
-	email.SendSignup(first, last, address, pass, s.Printer())
-	return nil
-}
-
-func resetPassword(s *router.Request) error {
-	p := s.Printer()
-	r := s.Request()
-	email.SendReset(r.PostFormValue("email"), p)
-	return nil
-}
-
-func emailForm(s *router.Request) error {
-	p := s.Printer()
-	r := s.Request()
-	r.ParseMultipartForm(10 << 20) // parse up to 10MB
-	if r.PostFormValue("name") == "" || r.PostFormValue("email") == "" {
-		return errors.New(p.Sprint("Missing name or email in form. Please contact us at olmaxmedical@gmail.com"))
-	}
-	if b, ok := r.Form["sendto"]; !ok || b[0] == "" {
-		return errors.New(p.Sprint("Missing value for target email. Please contact us at olmaxmedical.gmail.com"))
-	}
-	attachments := make(map[string]multipart.File)
-	m := r.MultipartForm
-	for _, headers := range m.File {
-		for _, header := range headers {
-			file, err := header.Open()
-			if err != nil { //non fatal, log any oddities and continue
-				log.Println(err)
-				continue
-			}
-			attachments[header.Filename] = file
-		}
-	}
-	email.SendForm(r.Form, p, attachments)
-	return nil
-}
--- a/errors.go
+++ /dev/null
@@ -1,28 +1,0 @@
-package plugins
-
-import (
-	"github.com/olmaxmedical/router"
-)
-
-// FormErrors - A list of errors present on a POST request
-const FormErrors router.PluginMask = 1 << 8
-
-func init() {
-	b := &router.Plugin{
-		Name:     "errors",
-		Run:      GetFormErrors,
-		Validate: nil,
-	}
-	router.AddPlugin(b, FormErrors)
-}
-
-// GetFormErrors - return the client a list of any errors in the form
-func GetFormErrors(r *router.Request) map[string]interface{} {
-	s := r.Session()
-	if s == nil {
-		return nil
-	}
-	return map[string]interface{}{
-		"errors": s.Get("errors"),
-	}
-}
--- a/go.mod
+++ /dev/null
@@ -1,13 +1,0 @@
-module github.com/olmaxmedical/plugins
-
-go 1.14
-
-require (
-	github.com/olmaxmedical/database v0.0.1
-	github.com/olmaxmedical/email v0.0.1
-	github.com/olmaxmedical/router v0.0.1
-	github.com/pariz/gountries v0.0.0-20191029140926-233bc78cf5b5
-	github.com/stretchr/testify v1.5.1 // indirect
-	golang.org/x/text v0.3.2
-	gopkg.in/yaml.v2 v2.2.8 // indirect
-)
--- a/go.sum
+++ /dev/null
@@ -1,30 +1,0 @@
-github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
-github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/olmaxmedical/database v0.0.0/go.mod h1:BPYEBAP3GYeSeqg5hOaH7GC8+P/VnD2OuHYounbVjQs=
-github.com/olmaxmedical/database v0.0.1 h1:cuocVljXq7cPRS9HygFg2B/WSdzWAZEHrc5uMnN+A0A=
-github.com/olmaxmedical/database v0.0.1/go.mod h1:/5Tl6/p0jpvLpj4GaoFki3wRG/3b+ipNNhM5Dyi6Zf8=
-github.com/olmaxmedical/email v0.0.1 h1:bhOERmPiUmFJqC133s+FFXucSI3dNnfDKsboDYFEbkc=
-github.com/olmaxmedical/email v0.0.1/go.mod h1:bz6en9uc6h9fyu3MW2jTwYW19ZclQ22JkcIxsl3/epc=
-github.com/olmaxmedical/router v0.0.1 h1:x3fZ9u00xwKxLvPj8Au0QhIxpmxRv+q2lVMdBmyRQjM=
-github.com/olmaxmedical/router v0.0.1/go.mod h1:28e377pByZCQMBAdERDBhX4wYeTsJgc6U4yOEiz7MsA=
-github.com/olmaxmedical/session v0.0.1 h1:2xdSjpEg89+ClRFLPp/gR3lNxl4JjPAUd2Hsds++FFs=
-github.com/olmaxmedical/session v0.0.1/go.mod h1:XOVyHL+cKa5t2fLDIJtFxwEzJOa3r1hUkwlL4aybdqA=
-github.com/pariz/gountries v0.0.0-20191029140926-233bc78cf5b5 h1:842t0ixg/A4my8/Q3oDNdHIsKYIx02NDlWVEhaiBToo=
-github.com/pariz/gountries v0.0.0-20191029140926-233bc78cf5b5/go.mod h1:U0ETmPPEsfd7CpUKNMYi68xIOL8Ww4jPZlaqNngcwqs=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/scorredoira/email v0.0.0-20191107070024-dc7b732c55da h1:hhmnjfzz7szp75AyXxn8tDfEA0oU4REQLmpuW6zNAOY=
-github.com/scorredoira/email v0.0.0-20191107070024-dc7b732c55da/go.mod h1:Q5ljvYIBpukMH+wgB8kcPV1i9NX8TqU++8GgBKq3pt0=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
--- a/messages.go
+++ /dev/null
@@ -1,29 +1,0 @@
-package plugins
-
-import (
-	"github.com/olmaxmedical/router"
-)
-
-// Messages - list all messages by key for use in a message template
-const Messages router.PluginMask = 1 << 9
-
-func init() {
-	b := &router.Plugin{
-		Name:     "messages",
-		Run:      GetMessages,
-		Validate: nil,
-	}
-	router.AddPlugin(b, Messages)
-}
-
-// GetMessages - return a map of message structs
-func GetMessages(r *router.Request) map[string]interface{} {
-	s := r.Session()
-	if s == nil {
-		return nil
-	}
-	return map[string]interface{}{
-		// No, this won't actually do anything.
-		"messages": s.Get("messages"),
-	}
-}
--- a/offers.go
+++ /dev/null
@@ -1,19 +1,0 @@
-package plugins
-
-import "github.com/olmaxmedical/router"
-
-// MakeOffer - Request a time slot with doctor
-const MakeOffer router.PluginMask = 1 << 10
-
-func init() {
-	b := &router.Plugin{
-		Name:     "offer",
-		Run:      nil,
-		Validate: offer,
-	}
-	router.AddPlugin(b, MakeOffer)
-}
-
-func offer(s *router.Request) error {
-	return nil
-}
--- a/password.go
+++ /dev/null
@@ -1,62 +1,0 @@
-package plugins
-
-import (
-	"errors"
-
-	"github.com/olmaxmedical/database"
-	"github.com/olmaxmedical/router"
-)
-
-// ValidateLogin - Check user/pass combo exists
-const ValidateLogin router.PluginMask = 1 << 11
-
-// ResetPassword - Update database entry for password
-const ResetPassword router.PluginMask = 1 << 12
-
-func init() {
-	b := &router.Plugin{
-		Name:     "login",
-		Run:      nil,
-		Validate: login,
-	}
-	router.AddPlugin(b, ValidateLogin)
-	c := &router.Plugin{
-		Name:     "setPassword",
-		Run:      nil,
-		Validate: setPass,
-	}
-	router.AddPlugin(c, ResetPassword)
-}
-
-func login(s *router.Request) error {
-	r := s.Request()
-	us := s.Session()
-	p := s.Printer()
-	user := r.PostFormValue("email")
-	pass := r.PostFormValue("pass")
-	if database.ValidateLogin(user, pass) {
-		us.Set("username", user)
-		us.Set("login", "true")
-		us.Set("role", database.UserRole(user))
-		us.Set("token", database.NewToken())
-		return nil
-	}
-	return errors.New(p.Sprint("Invalid login"))
-}
-
-func setPass(s *router.Request) error {
-	r := s.Request()
-	p := s.Printer()
-
-	pass := r.PostFormValue("password")
-	repeat := r.PostFormValue("reenter")
-	if pass != repeat {
-		return errors.New(p.Sprint("Passwords do not match"))
-	}
-	token := r.PostFormValue("token")
-	if !database.FindTempEntry(token) {
-		return errors.New(p.Sprint("Session expired"))
-	}
-	database.UpdateUserPassword(token, pass)
-	return nil
-}
--- /dev/null
+++ b/plugins/appointments.go
@@ -1,0 +1,25 @@
+package plugins
+
+import (
+	"github.com/olmaxmedical/router"
+)
+
+// AddAppointment registers an appointment into the appointment book
+// TODO(halfwit) message/email client to fill out Symptoms form
+const AddAppointment router.PluginMask = 1 << 1
+
+func init() {
+	b := &router.Plugin{
+		Name:     "Add Appointments",
+		Run:      nil,
+		Validate: addAppt,
+	}
+	router.AddPlugin(b, AddAppointment)
+}
+
+func addAppt(s *router.Request) error {
+	// get &id=
+	// Auth user and requisite key
+	// TODO(halfwit) create unique key in patient/appointments 
+	return nil
+}
--- /dev/null
+++ b/plugins/bookings.go
@@ -1,0 +1,22 @@
+package plugins
+
+import (
+	"github.com/olmaxmedical/router"
+)
+
+// ListBookings retreives a list of all upcoming bookings for a given doctor
+const ListBookings router.PluginMask = 1 << 2
+
+func init() {
+	b := &router.Plugin{
+		Name:     "List Bookings",
+		Run:      nil,
+		Validate: listBookings,
+	}
+	router.AddPlugin(b, ListBookings)
+}
+
+func listBookings(s *router.Request) error {
+	
+	return nil
+}
--- /dev/null
+++ b/plugins/countries.go
@@ -1,0 +1,96 @@
+package plugins
+
+import (
+	"fmt"
+	"sort"
+	"strings"
+
+	"github.com/olmaxmedical/router"
+	"github.com/pariz/gountries"
+	"golang.org/x/text/message"
+)
+
+// Countries - Populate a localized spinner to select country
+const Countries router.PluginMask = 1 << 3
+
+// Country - Mapping token to internationalized country code
+type Country struct {
+	ID   string
+	Name string
+}
+
+type countries struct {
+	list []gountries.Country
+}
+
+var cache *countries
+
+func init() {
+	var l []gountries.Country
+	c := gountries.New()
+	for _, c := range c.FindAllCountries() {
+		l = append(l, c)
+	}
+	cache = &countries{
+		list: l,
+	}
+	sort.Sort(cache)
+	b := &router.Plugin{
+		Name:     "country",
+		Run:      listCountries,
+		Validate: validateCountries,
+	}
+	router.AddPlugin(b, Countries)
+}
+
+// Len - For Sort implementation
+func (c *countries) Len() int {
+	return len(c.list)
+}
+
+// Less - For Sort implementation
+func (c *countries) Less(i, j int) bool {
+	switch strings.Compare(c.list[i].Name.Common, c.list[j].Name.Common) {
+	case -1:
+		return true
+	default:
+		return false
+	}
+}
+
+// Swap - For Sort implementation
+func (c *countries) Swap(i, j int) {
+	tmp := c.list[i]
+	c.list[i] = c.list[j]
+	c.list[j] = tmp
+}
+
+func listCountries(r *router.Request) map[string]interface{} {
+	p := r.Printer()
+	// TODO(halfwit): Use Request to get a localized country name
+	c := make(map[string]interface{})
+	c["label"] = p.Sprint("Select country/countries")
+	for _, item := range cache.list {
+		c[item.Name.Common] = item.Name.Common
+	}
+	return c
+}
+
+func validateCountries(r *router.Request) error {
+	s := r.Request()
+	for _, c := range s.PostFormValue("country") {
+		if msg := checkCountry(r.Printer(), c); msg != nil {
+			return msg
+		}
+	}
+	return nil
+}
+
+func checkCountry(p *message.Printer, country rune) error {
+	for _, item := range cache.list {
+		if item.Name.Common == string(country) {
+			return nil
+		}
+	}
+	return fmt.Errorf("%s", p.Sprint("No country entered/nil value entered"))
+}
--- /dev/null
+++ b/plugins/doctor.go
@@ -1,0 +1,55 @@
+package plugins
+
+import (
+	"github.com/olmaxmedical/router"
+)
+
+// ListDoctors - Bitmask to list doctors of in client country
+const ListDoctors router.PluginMask = 1 << 4
+
+type doctor struct {
+	Image     string
+	AlmaMater string
+	Name      string
+	Residency string
+	Current   string
+	Country   string
+	Specialty string
+	Rate      string
+}
+
+func init() {
+	b := &router.Plugin{
+		Name:     "doctors",
+		Run:      ListDocs,
+		Validate: nil,
+	}
+	router.AddPlugin(b, ListDoctors)
+}
+
+// ListDocs - Query db and return list of doctors in country
+// These may need eventual localization for any bilingual doctors we have
+func ListDocs(r *router.Request) map[string]interface{} {
+	return map[string]interface{}{
+		"Mark Abuzamzam, MD": &doctor{
+			Image:     "AbuzamzamMD.jpg",
+			AlmaMater: "University of Southern California School of Medicine",
+			Residency: "University of Southern California, San Diego. Internal Medicine Residency",
+			Name:      "Mark Abuzamzam, MD",
+			Current:   "Current Faculty at University of California Irvine Program Director",
+			Country:   "United States of America",
+			Specialty: "Internal Medicine and Addictions Medicine",
+			Rate:      "0.0013 BTC",
+		},
+		"Martha Woodfin, MD": &doctor{
+			Image:     "WoodfinMD.jpg",
+			Name:      "Martha Woodfin, MD",
+			AlmaMater: "University Seoul School of Medicine",
+			Residency: "University of Las Vegas Nevada, Pediatric Medicine Residency",
+			Current:   "Current Staff at Mercy Hospital Jackson NC",
+			Country:   "United States of America",
+			Specialty: "Internal Medicine",
+			Rate:      "0.0011 BTC",
+		},
+	}
+}
--- /dev/null
+++ b/plugins/email.go
@@ -1,0 +1,83 @@
+package plugins
+
+import (
+	"errors"
+	"log"
+	"mime/multipart"
+
+	"github.com/olmaxmedical/email"
+	"github.com/olmaxmedical/router"
+)
+
+// EmailForm - Patient form to gmail
+const EmailForm router.PluginMask = 1 << 5
+
+// SendSignup - Send account creation validation email
+const SendSignup router.PluginMask = 1 << 6
+
+// SendReset - Send password reset email
+const SendReset router.PluginMask = 1 << 7
+
+func init() {
+	b := &router.Plugin{
+		Name:     "emailform",
+		Run:      nil,
+		Validate: emailForm,
+	}
+	router.AddPlugin(b, EmailForm)
+	c := &router.Plugin{
+		Name:     "signupEmail",
+		Run:      nil,
+		Validate: signupEmail,
+	}
+	router.AddPlugin(c, SendSignup)
+	d := &router.Plugin{
+		Name:     "resetPassword",
+		Run:      nil,
+		Validate: resetPassword,
+	}
+	router.AddPlugin(d, SendReset)
+}
+
+func signupEmail(s *router.Request) error {
+	r := s.Request()
+	first := r.PostFormValue("fname")
+	last := r.PostFormValue("lname")
+	address := r.PostFormValue("email")
+	pass := r.PostFormValue("pass")
+	email.SendSignup(first, last, address, pass, s.Printer())
+	return nil
+}
+
+func resetPassword(s *router.Request) error {
+	p := s.Printer()
+	r := s.Request()
+	email.SendReset(r.PostFormValue("email"), p)
+	return nil
+}
+
+func emailForm(s *router.Request) error {
+	p := s.Printer()
+	r := s.Request()
+	r.ParseMultipartForm(10 << 20) // parse up to 10MB
+	if r.PostFormValue("name") == "" || r.PostFormValue("email") == "" {
+		return errors.New(p.Sprint("Missing name or email in form. Please contact us at olmaxmedical@gmail.com"))
+	}
+	if b, ok := r.Form["sendto"]; !ok || b[0] == "" {
+		return errors.New(p.Sprint("Missing value for target email. Please contact us at olmaxmedical.gmail.com"))
+	}
+	attachments := make(map[string]multipart.File)
+	m := r.MultipartForm
+	for _, headers := range m.File {
+		for _, header := range headers {
+			file, err := header.Open()
+			if err != nil { //non fatal, log any oddities and continue
+				log.Println(err)
+				continue
+			}
+			attachments[header.Filename] = file
+		}
+	}
+	email.SendForm(r.Form, p, attachments)
+	return nil
+}
--- /dev/null
+++ b/plugins/errors.go
@@ -1,0 +1,28 @@
+package plugins
+
+import (
+	"github.com/olmaxmedical/router"
+)
+
+// FormErrors - A list of errors present on a POST request
+const FormErrors router.PluginMask = 1 << 8
+
+func init() {
+	b := &router.Plugin{
+		Name:     "errors",
+		Run:      GetFormErrors,
+		Validate: nil,
+	}
+	router.AddPlugin(b, FormErrors)
+}
+
+// GetFormErrors - return the client a list of any errors in the form
+func GetFormErrors(r *router.Request) map[string]interface{} {
+	s := r.Session()
+	if s == nil {
+		return nil
+	}
+	return map[string]interface{}{
+		"errors": s.Get("errors"),
+	}
+}
--- /dev/null
+++ b/plugins/messages.go
@@ -1,0 +1,29 @@
+package plugins
+
+import (
+	"github.com/olmaxmedical/router"
+)
+
+// Messages - list all messages by key for use in a message template
+const Messages router.PluginMask = 1 << 9
+
+func init() {
+	b := &router.Plugin{
+		Name:     "messages",
+		Run:      GetMessages,
+		Validate: nil,
+	}
+	router.AddPlugin(b, Messages)
+}
+
+// GetMessages - return a map of message structs
+func GetMessages(r *router.Request) map[string]interface{} {
+	s := r.Session()
+	if s == nil {
+		return nil
+	}
+	return map[string]interface{}{
+		// No, this won't actually do anything.
+		"messages": s.Get("messages"),
+	}
+}
--- /dev/null
+++ b/plugins/offers.go
@@ -1,0 +1,19 @@
+package plugins
+
+import "github.com/olmaxmedical/router"
+
+// MakeOffer - Request a time slot with doctor
+const MakeOffer router.PluginMask = 1 << 10
+
+func init() {
+	b := &router.Plugin{
+		Name:     "offer",
+		Run:      nil,
+		Validate: offer,
+	}
+	router.AddPlugin(b, MakeOffer)
+}
+
+func offer(s *router.Request) error {
+	return nil
+}
--- /dev/null
+++ b/plugins/password.go
@@ -1,0 +1,62 @@
+package plugins
+
+import (
+	"errors"
+
+	"github.com/olmaxmedical/database"
+	"github.com/olmaxmedical/router"
+)
+
+// ValidateLogin - Check user/pass combo exists
+const ValidateLogin router.PluginMask = 1 << 11
+
+// ResetPassword - Update database entry for password
+const ResetPassword router.PluginMask = 1 << 12
+
+func init() {
+	b := &router.Plugin{
+		Name:     "login",
+		Run:      nil,
+		Validate: login,
+	}
+	router.AddPlugin(b, ValidateLogin)
+	c := &router.Plugin{
+		Name:     "setPassword",
+		Run:      nil,
+		Validate: setPass,
+	}
+	router.AddPlugin(c, ResetPassword)
+}
+
+func login(s *router.Request) error {
+	r := s.Request()
+	us := s.Session()
+	p := s.Printer()
+	user := r.PostFormValue("email")
+	pass := r.PostFormValue("pass")
+	if database.ValidateLogin(user, pass) {
+		us.Set("username", user)
+		us.Set("login", "true")
+		us.Set("role", database.UserRole(user))
+		us.Set("token", database.NewToken())
+		return nil
+	}
+	return errors.New(p.Sprint("Invalid login"))
+}
+
+func setPass(s *router.Request) error {
+	r := s.Request()
+	p := s.Printer()
+
+	pass := r.PostFormValue("password")
+	repeat := r.PostFormValue("reenter")
+	if pass != repeat {
+		return errors.New(p.Sprint("Passwords do not match"))
+	}
+	token := r.PostFormValue("token")
+	if !database.FindTempEntry(token) {
+		return errors.New(p.Sprint("Session expired"))
+	}
+	database.UpdateUserPassword(token, pass)
+	return nil
+}
--- /dev/null
+++ b/plugins/search.go
@@ -1,0 +1,20 @@
+package plugins
+
+import "github.com/olmaxmedical/router"
+
+// Search - generic search for doctors in area
+const Search router.PluginMask = 1 << 13
+
+func init() {
+	b := &router.Plugin{
+		Name:     "search",
+		Run:      nil,
+		Validate: search,
+	}
+	router.AddPlugin(b, Search)
+}
+
+// Stuuuuubbb
+func search(r *router.Request) error {
+	return nil
+}
--- /dev/null
+++ b/plugins/services.go
@@ -1,0 +1,114 @@
+package plugins
+
+import (
+	"fmt"
+
+	"github.com/olmaxmedical/router"
+)
+
+// Services - Bitmask to list services in native language
+const Services router.PluginMask = 1 << 14
+
+func init() {
+	b := &router.Plugin{
+		Name:     "specialties",
+		Run:      ListServices,
+		Validate: ValidateServices,
+	}
+	router.AddPlugin(b, Services)
+}
+
+// ValidateServices - Ensure the specialties entered exist in our map
+func ValidateServices(r *router.Request) error {
+	s := r.Request()
+	var errs []string
+	for _, entry := range s.PostFormValue("specialty") {
+		switch string(entry) {
+		case "acutepain":
+		case "anasthesiology":
+		case "bariatric":
+		case "cardiology":
+		case "chiropractic":
+		case "chronic":
+		case "critcare":
+		case "dermatology":
+		case "emergency":
+		case "endocrinology":
+		case "otolaringology":
+		case "familymedicine":
+		case "gastro":
+		case "headneck":
+		case "hematology":
+		case "hepatology":
+		case "hyperbaric":
+		case "immunology":
+		case "diseases":
+		case "internal":
+		case "neonatal":
+		case "nephrology":
+		case "neurology":
+		case "neurosurgery":
+		case "obstetrics":
+		case "occupational":
+		case "opthamology":
+		case "orthopedics":
+		case "palliative":
+		case "pediatrics":
+		case "podiatry":
+		case "pulmonology":
+		case "radiology":
+		case "radiation":
+		case "transplants":
+			continue
+		default:
+			errs = append(errs, fmt.Sprintf("Unknown entry: %q\n", entry))
+		}
+		if len(errs) > 0 {
+			return fmt.Errorf("%s", errs)
+		}
+	}
+	return nil
+}
+
+// ListServices - return a list of native language representations of our medical fields
+func ListServices(r *router.Request) map[string]interface{} {
+	p := r.Printer()
+	return map[string]interface{}{
+		"label":          p.Sprint("Select specialty/specialties"),
+		"acutepain":      p.Sprint("Acute Pain Medicine"),
+		"anesthesiology": p.Sprint("Anesthesiology"),
+		"bariatric":      p.Sprint("Bariatric Surgery"),
+		"cardiology":     p.Sprint("Cardiology"),
+		"chiropractic":   p.Sprint("Chiropractics"),
+		"chronic":        p.Sprint("Chronic Pain"),
+		"critcare":       p.Sprint("Critical Care"),
+		"dermatology":    p.Sprint("Dermatology"),
+		"emergency":      p.Sprint("Emergency Medicine"),
+		"endocrinology":  p.Sprint("Endocrinology"),
+		"otolaringology": p.Sprint("Ear Nose and Throat"),
+		"familymedicine": p.Sprint("Family Medicine"),
+		"gastro":         p.Sprint("Gastrointestinology"),
+		"headneck":       p.Sprint("Head and Neck"),
+		"hematology":     p.Sprint("Hematology and Oncology"),
+		"hepatology":     p.Sprint("Hepatology"),
+		"hyperbaric":     p.Sprint("Hyperbaric"),
+		"immunology":     p.Sprint("Immunology"),
+		"diseases":       p.Sprint("Infectious Diseases"),
+		"internal":       p.Sprint("Internal Medicine"),
+		"neonatal":       p.Sprint("Neonatology"),
+		"nephrology":     p.Sprint("Nephrology"),
+		"neurology":      p.Sprint("Neurology"),
+		"neurosurgery":   p.Sprint("Neurosurgery"),
+		"obstetrics":     p.Sprint("Obstetrics and Gynecology"),
+		"occupational":   p.Sprint("Occupational Medicine"),
+		"opthamology":    p.Sprint("Opthamology"),
+		"orthopedics":    p.Sprint("Orthopedic Surgery"),
+		"palliative":     p.Sprint("Palliative Care"),
+		"pediatrics":     p.Sprint("Pediatrics"),
+		"podiatry":       p.Sprint("Podiatry"),
+		"pulmonology":    p.Sprint("Pulmonology"),
+		"radiology":      p.Sprint("Radiology"),
+		"radiation":      p.Sprint("Radiaton Oncology"),
+		"transplants":    p.Sprint("Transplant Surgery"),
+	}
+}
--- /dev/null
+++ b/plugins/tokens.go
@@ -1,0 +1,29 @@
+package plugins
+
+import (
+	"github.com/olmaxmedical/database"
+	"github.com/olmaxmedical/router"
+)
+
+// FormToken - A database-persisted one time use token to relate forms to POST requests
+const FormToken router.PluginMask = 1 << 15
+
+func init() {
+	c := &router.Plugin{
+		Name:     "formToken",
+		Run:      newFormToken,
+		Validate: validateToken,
+	}
+	router.AddPlugin(c, FormToken)
+}
+
+func validateToken(r *router.Request) error {
+	return database.ValidateToken(r.Request(), r.Session())
+}
+
+// TODO(halfwit) - database
+func newFormToken(r *router.Request) map[string]interface{} {
+	return map[string]interface{}{
+		"token": database.NewToken(),
+	}
+}
--- a/search.go
+++ /dev/null
@@ -1,20 +1,0 @@
-package plugins
-
-import "github.com/olmaxmedical/router"
-
-// Search - generic search for doctors in area
-const Search router.PluginMask = 1 << 13
-
-func init() {
-	b := &router.Plugin{
-		Name:     "search",
-		Run:      nil,
-		Validate: search,
-	}
-	router.AddPlugin(b, Search)
-}
-
-// Stuuuuubbb
-func search(r *router.Request) error {
-	return nil
-}
--- a/services.go
+++ /dev/null
@@ -1,114 +1,0 @@
-package plugins
-
-import (
-	"fmt"
-
-	"github.com/olmaxmedical/router"
-)
-
-// Services - Bitmask to list services in native language
-const Services router.PluginMask = 1 << 14
-
-func init() {
-	b := &router.Plugin{
-		Name:     "specialties",
-		Run:      ListServices,
-		Validate: ValidateServices,
-	}
-	router.AddPlugin(b, Services)
-}
-
-// ValidateServices - Ensure the specialties entered exist in our map
-func ValidateServices(r *router.Request) error {
-	s := r.Request()
-	var errs []string
-	for _, entry := range s.PostFormValue("specialty") {
-		switch string(entry) {
-		case "acutepain":
-		case "anasthesiology":
-		case "bariatric":
-		case "cardiology":
-		case "chiropractic":
-		case "chronic":
-		case "critcare":
-		case "dermatology":
-		case "emergency":
-		case "endocrinology":
-		case "otolaringology":
-		case "familymedicine":
-		case "gastro":
-		case "headneck":
-		case "hematology":
-		case "hepatology":
-		case "hyperbaric":
-		case "immunology":
-		case "diseases":
-		case "internal":
-		case "neonatal":
-		case "nephrology":
-		case "neurology":
-		case "neurosurgery":
-		case "obstetrics":
-		case "occupational":
-		case "opthamology":
-		case "orthopedics":
-		case "palliative":
-		case "pediatrics":
-		case "podiatry":
-		case "pulmonology":
-		case "radiology":
-		case "radiation":
-		case "transplants":
-			continue
-		default:
-			errs = append(errs, fmt.Sprintf("Unknown entry: %q\n", entry))
-		}
-		if len(errs) > 0 {
-			return fmt.Errorf("%s", errs)
-		}
-	}
-	return nil
-}
-
-// ListServices - return a list of native language representations of our medical fields
-func ListServices(r *router.Request) map[string]interface{} {
-	p := r.Printer()
-	return map[string]interface{}{
-		"label":          p.Sprint("Select specialty/specialties"),
-		"acutepain":      p.Sprint("Acute Pain Medicine"),
-		"anesthesiology": p.Sprint("Anesthesiology"),
-		"bariatric":      p.Sprint("Bariatric Surgery"),
-		"cardiology":     p.Sprint("Cardiology"),
-		"chiropractic":   p.Sprint("Chiropractics"),
-		"chronic":        p.Sprint("Chronic Pain"),
-		"critcare":       p.Sprint("Critical Care"),
-		"dermatology":    p.Sprint("Dermatology"),
-		"emergency":      p.Sprint("Emergency Medicine"),
-		"endocrinology":  p.Sprint("Endocrinology"),
-		"otolaringology": p.Sprint("Ear Nose and Throat"),
-		"familymedicine": p.Sprint("Family Medicine"),
-		"gastro":         p.Sprint("Gastrointestinology"),
-		"headneck":       p.Sprint("Head and Neck"),
-		"hematology":     p.Sprint("Hematology and Oncology"),
-		"hepatology":     p.Sprint("Hepatology"),
-		"hyperbaric":     p.Sprint("Hyperbaric"),
-		"immunology":     p.Sprint("Immunology"),
-		"diseases":       p.Sprint("Infectious Diseases"),
-		"internal":       p.Sprint("Internal Medicine"),
-		"neonatal":       p.Sprint("Neonatology"),
-		"nephrology":     p.Sprint("Nephrology"),
-		"neurology":      p.Sprint("Neurology"),
-		"neurosurgery":   p.Sprint("Neurosurgery"),
-		"obstetrics":     p.Sprint("Obstetrics and Gynecology"),
-		"occupational":   p.Sprint("Occupational Medicine"),
-		"opthamology":    p.Sprint("Opthamology"),
-		"orthopedics":    p.Sprint("Orthopedic Surgery"),
-		"palliative":     p.Sprint("Palliative Care"),
-		"pediatrics":     p.Sprint("Pediatrics"),
-		"podiatry":       p.Sprint("Podiatry"),
-		"pulmonology":    p.Sprint("Pulmonology"),
-		"radiology":      p.Sprint("Radiology"),
-		"radiation":      p.Sprint("Radiaton Oncology"),
-		"transplants":    p.Sprint("Transplant Surgery"),
-	}
-}
--- a/tokens.go
+++ /dev/null
@@ -1,29 +1,0 @@
-package plugins
-
-import (
-	"github.com/olmaxmedical/database"
-	"github.com/olmaxmedical/router"
-)
-
-// FormToken - A database-persisted one time use token to relate forms to POST requests
-const FormToken router.PluginMask = 1 << 15
-
-func init() {
-	c := &router.Plugin{
-		Name:     "formToken",
-		Run:      newFormToken,
-		Validate: validateToken,
-	}
-	router.AddPlugin(c, FormToken)
-}
-
-func validateToken(r *router.Request) error {
-	return database.ValidateToken(r.Request(), r.Session())
-}
-
-// TODO(halfwit) - database
-func newFormToken(r *router.Request) map[string]interface{} {
-	return map[string]interface{}{
-		"token": database.NewToken(),
-	}
-}