Browse Source

Rename project to alps

master
Simon Ser 4 years ago
parent
commit
b891a95fcf
No known key found for this signature in database GPG Key ID: FDE7BE0E88F5E48
52 changed files with 218 additions and 218 deletions
  1. +8
    -8
      README.md
  2. +10
    -10
      cmd/alps/main.go
  3. +2
    -2
      contrib/hotreload.sh
  4. +1
    -1
      discover.go
  5. +4
    -4
      docs/cli.md
  6. +9
    -9
      docs/example-go-plugin/plugin.go
  7. +3
    -3
      docs/example-lua-plugin/main.lua
  8. +6
    -6
      docs/google.md
  9. +4
    -4
      docs/themes-and-plugins.md
  10. +1
    -1
      go.mod
  11. +1
    -1
      imap.go
  12. +2
    -2
      plugin.go
  13. +2
    -2
      plugin_go.go
  14. +1
    -1
      plugins/base/imap.go
  15. +4
    -4
      plugins/base/plugin.go
  16. +1
    -1
      plugins/base/public/compose.html
  17. +1
    -1
      plugins/base/public/head.html
  18. +1
    -1
      plugins/base/public/login.html
  19. +1
    -1
      plugins/base/public/mailbox.html
  20. +1
    -1
      plugins/base/public/message.html
  21. +1
    -1
      plugins/base/public/settings.html
  22. +34
    -34
      plugins/base/routes.go
  23. +1
    -1
      plugins/base/smtp.go
  24. +1
    -1
      plugins/base/strconv.go
  25. +1
    -1
      plugins/base/template.go
  26. +4
    -4
      plugins/base/viewer.go
  27. +5
    -5
      plugins/caldav/caldav.go
  28. +7
    -7
      plugins/caldav/plugin.go
  29. +1
    -1
      plugins/caldav/public/calendar.html
  30. +1
    -1
      plugins/caldav/public/event.html
  31. +9
    -9
      plugins/caldav/routes.go
  32. +4
    -4
      plugins/carddav/carddav.go
  33. +13
    -13
      plugins/carddav/plugin.go
  34. +1
    -1
      plugins/carddav/public/address-book.html
  35. +1
    -1
      plugins/carddav/public/address-object.html
  36. +1
    -1
      plugins/carddav/public/update-address-object.html
  37. +11
    -11
      plugins/carddav/routes.go
  38. +9
    -9
      plugins/lua/lua.go
  39. +3
    -3
      plugins/lua/plugin.go
  40. +8
    -8
      plugins/viewhtml/plugin.go
  41. +3
    -3
      plugins/viewhtml/sanitize.go
  42. +6
    -6
      plugins/viewhtml/viewer.go
  43. +4
    -4
      plugins/viewtext/plugin.go
  44. +6
    -6
      plugins/viewtext/viewer.go
  45. +2
    -2
      renderer.go
  46. +4
    -4
      server.go
  47. +1
    -1
      session.go
  48. +1
    -1
      smtp.go
  49. +9
    -9
      store.go
  50. +1
    -1
      themes/alps/login.html
  51. +1
    -1
      themes/sourcehut/head.html
  52. +1
    -1
      themes/sourcehut/nav.html

+ 8
- 8
README.md View File

@@ -1,6 +1,6 @@
# koushin
# alps

[![GoDoc](https://godoc.org/git.sr.ht/~emersion/koushin?status.svg)](https://godoc.org/git.sr.ht/~emersion/koushin)
[![GoDoc](https://godoc.org/git.sr.ht/~emersion/alps?status.svg)](https://godoc.org/git.sr.ht/~emersion/alps)

A simple and extensible webmail.

@@ -8,17 +8,17 @@ A simple and extensible webmail.

Assuming SRV DNS records are properly set up (see [RFC 6186]):

go run ./cmd/koushin example.org
go run ./cmd/alps example.org

To manually specify upstream servers:

go run ./cmd/koushin imaps://mail.example.org:993 smtps://mail.example.org:465
go run ./cmd/alps imaps://mail.example.org:993 smtps://mail.example.org:465

Add `-theme sourcehut` to use the SourceHut theme. See `docs/cli.md` for more
information.

When developing themes and plugins, the script `contrib/hotreload.sh` can be
used to automatically reload koushin on file changes.
used to automatically reload alps on file changes.

## Contributing

@@ -29,6 +29,6 @@ Send patches on the [mailing list], report bugs on the [issue tracker].
MIT

[RFC 6186]: https://tools.ietf.org/html/rfc6186
[Go plugin helpers]: https://godoc.org/git.sr.ht/~emersion/koushin#GoPlugin
[mailing list]: https://lists.sr.ht/~sircmpwn/koushin
[issue tracker]: https://todo.sr.ht/~sircmpwn/koushin
[Go plugin helpers]: https://godoc.org/git.sr.ht/~emersion/alps#GoPlugin
[mailing list]: https://lists.sr.ht/~sircmpwn/alps
[issue tracker]: https://todo.sr.ht/~sircmpwn/alps

cmd/koushin/main.go → cmd/alps/main.go View File

@@ -7,28 +7,28 @@ import (
"os/signal"
"syscall"

"git.sr.ht/~emersion/koushin"
"git.sr.ht/~emersion/alps"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/labstack/gommon/log"

_ "git.sr.ht/~emersion/koushin/plugins/base"
_ "git.sr.ht/~emersion/koushin/plugins/caldav"
_ "git.sr.ht/~emersion/koushin/plugins/carddav"
_ "git.sr.ht/~emersion/koushin/plugins/lua"
_ "git.sr.ht/~emersion/koushin/plugins/viewhtml"
_ "git.sr.ht/~emersion/koushin/plugins/viewtext"
_ "git.sr.ht/~emersion/alps/plugins/base"
_ "git.sr.ht/~emersion/alps/plugins/caldav"
_ "git.sr.ht/~emersion/alps/plugins/carddav"
_ "git.sr.ht/~emersion/alps/plugins/lua"
_ "git.sr.ht/~emersion/alps/plugins/viewhtml"
_ "git.sr.ht/~emersion/alps/plugins/viewtext"
)

func main() {
var options koushin.Options
var options alps.Options
var addr string
flag.StringVar(&options.Theme, "theme", "", "default theme")
flag.StringVar(&addr, "addr", ":1323", "listening address")
flag.BoolVar(&options.Debug, "debug", false, "enable debug logs")

flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), "usage: koushin [options...] <upstream servers...>\n")
fmt.Fprintf(flag.CommandLine.Output(), "usage: alps [options...] <upstream servers...>\n")
flag.PrintDefaults()
}

@@ -45,7 +45,7 @@ func main() {
if l, ok := e.Logger.(*log.Logger); ok {
l.SetHeader("${time_rfc3339} ${level}")
}
s, err := koushin.New(e, &options)
s, err := alps.New(e, &options)
if err != nil {
e.Logger.Fatal(err)
}

+ 2
- 2
contrib/hotreload.sh View File

@@ -1,6 +1,6 @@
#!/bin/sh

# Watch themes and plugins files, automatically reload koushin on change.
# Watch themes and plugins files, automatically reload alps on change.

events=modify,create,delete,move
targets="themes/ plugins/"
@@ -8,6 +8,6 @@ targets="themes/ plugins/"
inotifywait -e "$events" -m -r $targets | while read line; do
jobs >/dev/null # Reap status of any terminated job
if [ -z "$(jobs)" ]; then
(sleep 0.5 && pkill -USR1 koushin) &
(sleep 0.5 && pkill -USR1 alps) &
fi
done

+ 1
- 1
discover.go View File

@@ -1,4 +1,4 @@
package koushin
package alps

import (
"fmt"


+ 4
- 4
docs/cli.md View File

@@ -1,22 +1,22 @@
# SYNOPSIS

koushin [options...] <upstream servers...>
alps [options...] <upstream servers...>

# DESCRIPTION

koushin is a simple and extensible webmail. It offers a web interface for IMAP,
alps is a simple and extensible webmail. It offers a web interface for IMAP,
SMTP and other upstream servers.

At least one upstream IMAP server needs to be specified. The easiest way to do
so is to just specify a domain name:

koushin example.org
alps example.org

This assumes SRV DNS records are properly set up (see [RFC 6186]).

Alternatively, one or more upstream server URLs can be specified:

koushin imaps://mail.example.org:993 smtps://mail.example.org:465
alps imaps://mail.example.org:993 smtps://mail.example.org:465

The following URL schemes are supported:



+ 9
- 9
docs/example-go-plugin/plugin.go View File

@@ -1,22 +1,22 @@
// Package exampleplugin is an example Go plugin for koushin.
// Package exampleplugin is an example Go plugin for alps.
//
// To enable it, import this package from cmd/koushin/main.go.
// To enable it, import this package from cmd/alps/main.go.
package exampleplugin

import (
"fmt"
"net/http"

"git.sr.ht/~emersion/koushin"
koushinbase "git.sr.ht/~emersion/koushin/plugins/base"
"git.sr.ht/~emersion/alps"
alpsbase "git.sr.ht/~emersion/alps/plugins/base"
)

func init() {
p := koushin.GoPlugin{Name: "example"}
p := alps.GoPlugin{Name: "example"}

// Setup a function called when the mailbox view is rendered
p.Inject("mailbox.html", func(ctx *koushin.Context, kdata koushin.RenderData) error {
data := kdata.(*koushinbase.MailboxRenderData)
p.Inject("mailbox.html", func(ctx *alps.Context, kdata alps.RenderData) error {
data := kdata.(*alpsbase.MailboxRenderData)
fmt.Println("The mailbox view for " + data.Mailbox.Name + " is being rendered")
// Set extra data that can be accessed from the mailbox.html template
data.Extra["Example"] = "Hi from Go"
@@ -24,7 +24,7 @@ func init() {
})

// Wire up a new route
p.GET("/example", func(ctx *koushin.Context) error {
p.GET("/example", func(ctx *alps.Context) error {
return ctx.String(http.StatusOK, "This is an example page.")
})

@@ -35,5 +35,5 @@ func init() {
},
})

koushin.RegisterPluginLoader(p.Loader())
alps.RegisterPluginLoader(p.Loader())
}

+ 3
- 3
docs/example-lua-plugin/main.lua View File

@@ -2,18 +2,18 @@
print("Hi, this is an example Lua plugin")

-- Setup a function called when the mailbox view is rendered
koushin.on_render("mailbox.html", function(data)
alps.on_render("mailbox.html", function(data)
print("The mailbox view for " .. data.Mailbox.Name .. " is being rendered")
-- Set extra data that can be accessed from the mailbox.html template
data.Extra.Example = "Hi from Lua"
end)

-- Wire up a new route
koushin.set_route("GET", "/example", function(ctx)
alps.set_route("GET", "/example", function(ctx)
ctx:String(200, "This is an example page.")
end)

-- Set a filter function that can be used from templates
koushin.set_filter("example_and", function(a, b)
alps.set_filter("example_and", function(a, b)
return a .. " and " .. b
end)

+ 6
- 6
docs/google.md View File

@@ -1,21 +1,21 @@
# Running koushin with a Google account
# Running alps with a Google account

## Create an application password

First, you'll need to obtain an application-specific password for koushin from
First, you'll need to obtain an application-specific password for alps from
the [app passwords] page on your Google account.

## Run koushin
## Run alps

Start koushin with these upstream URLs:
Start alps with these upstream URLs:

koushin imaps://imap.gmail.com smtps://smtp.gmail.com \
alps imaps://imap.gmail.com smtps://smtp.gmail.com \
carddavs://www.googleapis.com/carddav/v1/principals/YOUREMAIL/ \
caldavs://www.google.com/calendar/dav

Replace `YOUREMAIL` with your Google account's e-mail address.

Once koushin is started, you can login with your e-mail address and the app
Once alps is started, you can login with your e-mail address and the app
password.

[app passwords]: https://security.google.com/settings/security/apppasswords

+ 4
- 4
docs/themes-and-plugins.md View File

@@ -17,7 +17,7 @@ Assets in `plugins/<name>/public/assets/*` are served by the HTTP server at
## Go plugins

They can use the [Go plugin helpers] and need to be included at compile-time in
`cmd/koushin/main.go`.
`cmd/alps/main.go`.

## Lua plugins

@@ -25,8 +25,8 @@ The entry point is at `plugins/<name>/main.lua`.

API:

* `koushin.on_render(name, f)`: prior to rendering the template `name`, call
* `alps.on_render(name, f)`: prior to rendering the template `name`, call
`f` with the template data (the special name `*` matches all templates)
* `koushin.set_filter(name, f)`: set a template function
* `koushin.set_route(method, path, f)`: register a new HTTP route, `f` will be
* `alps.set_filter(name, f)`: set a template function
* `alps.set_route(method, path, f)`: register a new HTTP route, `f` will be
called with the HTTP context

+ 1
- 1
go.mod View File

@@ -1,4 +1,4 @@
module git.sr.ht/~emersion/koushin
module git.sr.ht/~emersion/alps

go 1.13



+ 1
- 1
imap.go View File

@@ -1,4 +1,4 @@
package koushin
package alps

import (
"fmt"


+ 2
- 2
plugin.go View File

@@ -1,4 +1,4 @@
package koushin
package alps

import (
"html/template"
@@ -9,7 +9,7 @@ import (
// PluginDir is the path to the plugins directory.
const PluginDir = "plugins"

// Plugin extends koushin with additional functionality.
// Plugin extends alps with additional functionality.
type Plugin interface {
// Name should return the plugin name.
Name() string


+ 2
- 2
plugin_go.go View File

@@ -1,4 +1,4 @@
package koushin
package alps

import (
"html/template"
@@ -71,7 +71,7 @@ type goPluginRoute struct {
//
// p := GoPlugin{Name: "my-plugin"}
// // Define routes, template functions, etc
// koushin.RegisterPluginLoader(p.Loader())
// alps.RegisterPluginLoader(p.Loader())
type GoPlugin struct {
Name string



+ 1
- 1
plugins/base/imap.go View File

@@ -1,4 +1,4 @@
package koushinbase
package alpsbase

import (
"bufio"


+ 4
- 4
plugins/base/plugin.go View File

@@ -1,14 +1,14 @@
package koushinbase
package alpsbase

import (
"git.sr.ht/~emersion/koushin"
"git.sr.ht/~emersion/alps"
)

func init() {
p := koushin.GoPlugin{Name: "base"}
p := alps.GoPlugin{Name: "base"}

p.TemplateFuncs(templateFuncs)
registerRoutes(&p)

koushin.RegisterPluginLoader(p.Loader())
alps.RegisterPluginLoader(p.Loader())
}

+ 1
- 1
plugins/base/public/compose.html View File

@@ -1,6 +1,6 @@
{{template "head.html"}}

<h1>koushin</h1>
<h1>alps</h1>

<p>
<a href="/mailbox/INBOX">Back</a>


+ 1
- 1
plugins/base/public/head.html View File

@@ -2,6 +2,6 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>koushin</title>
<title>alps</title>
</head>
<body>

+ 1
- 1
plugins/base/public/login.html View File

@@ -1,6 +1,6 @@
{{template "head.html"}}

<h1>koushin</h1>
<h1>alps</h1>

<form method="post" action="">
<label for="username">Username:</label>


+ 1
- 1
plugins/base/public/mailbox.html View File

@@ -1,6 +1,6 @@
{{template "head.html"}}

<h1>koushin</h1>
<h1>alps</h1>

<p>
<a href="/logout">Logout</a>


+ 1
- 1
plugins/base/public/message.html View File

@@ -1,6 +1,6 @@
{{template "head.html"}}

<h1>koushin</h1>
<h1>alps</h1>

<p>
<a href="/mailbox/{{.Mailbox.Name | pathescape}}?page={{.MailboxPage}}">


+ 1
- 1
plugins/base/public/settings.html View File

@@ -1,6 +1,6 @@
{{template "head.html"}}

<h1>koushin</h1>
<h1>alps</h1>

<p>
<a href="/mailbox/INBOX">Back</a>


+ 34
- 34
plugins/base/routes.go View File

@@ -1,4 +1,4 @@
package koushinbase
package alpsbase

import (
"bytes"
@@ -11,7 +11,7 @@ import (
"strconv"
"strings"

"git.sr.ht/~emersion/koushin"
"git.sr.ht/~emersion/alps"
"github.com/emersion/go-imap"
imapmove "github.com/emersion/go-imap-move"
imapclient "github.com/emersion/go-imap/client"
@@ -21,18 +21,18 @@ import (
"github.com/labstack/echo/v4"
)

func registerRoutes(p *koushin.GoPlugin) {
p.GET("/", func(ctx *koushin.Context) error {
func registerRoutes(p *alps.GoPlugin) {
p.GET("/", func(ctx *alps.Context) error {
return ctx.Redirect(http.StatusFound, "/mailbox/INBOX")
})

p.GET("/mailbox/:mbox", handleGetMailbox)
p.POST("/mailbox/:mbox", handleGetMailbox)

p.GET("/message/:mbox/:uid", func(ctx *koushin.Context) error {
p.GET("/message/:mbox/:uid", func(ctx *alps.Context) error {
return handleGetPart(ctx, false)
})
p.GET("/message/:mbox/:uid/raw", func(ctx *koushin.Context) error {
p.GET("/message/:mbox/:uid/raw", func(ctx *alps.Context) error {
return handleGetPart(ctx, true)
})

@@ -64,7 +64,7 @@ func registerRoutes(p *koushin.GoPlugin) {
}

type MailboxRenderData struct {
koushin.BaseRenderData
alps.BaseRenderData
Mailbox *MailboxStatus
Mailboxes []MailboxInfo
Messages []IMAPMessage
@@ -72,7 +72,7 @@ type MailboxRenderData struct {
Query string
}

func handleGetMailbox(ctx *koushin.Context) error {
func handleGetMailbox(ctx *alps.Context) error {
mboxName, err := url.PathUnescape(ctx.Param("mbox"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err)
@@ -136,7 +136,7 @@ func handleGetMailbox(ctx *koushin.Context) error {
}

return ctx.Render(http.StatusOK, "mailbox.html", &MailboxRenderData{
BaseRenderData: *koushin.NewBaseRenderData(ctx),
BaseRenderData: *alps.NewBaseRenderData(ctx),
Mailbox: mbox,
Mailboxes: mailboxes,
Messages: msgs,
@@ -146,13 +146,13 @@ func handleGetMailbox(ctx *koushin.Context) error {
})
}

func handleLogin(ctx *koushin.Context) error {
func handleLogin(ctx *alps.Context) error {
username := ctx.FormValue("username")
password := ctx.FormValue("password")
if username != "" && password != "" {
s, err := ctx.Server.Sessions.Put(username, password)
if err != nil {
if _, ok := err.(koushin.AuthError); ok {
if _, ok := err.(alps.AuthError); ok {
return ctx.Render(http.StatusOK, "login.html", nil)
}
return fmt.Errorf("failed to put connection in pool: %v", err)
@@ -165,17 +165,17 @@ func handleLogin(ctx *koushin.Context) error {
return ctx.Redirect(http.StatusFound, "/mailbox/INBOX")
}

return ctx.Render(http.StatusOK, "login.html", koushin.NewBaseRenderData(ctx))
return ctx.Render(http.StatusOK, "login.html", alps.NewBaseRenderData(ctx))
}

func handleLogout(ctx *koushin.Context) error {
func handleLogout(ctx *alps.Context) error {
ctx.Session.Close()
ctx.SetSession(nil)
return ctx.Redirect(http.StatusFound, "/login")
}

type MessageRenderData struct {
koushin.BaseRenderData
alps.BaseRenderData
Mailboxes []MailboxInfo
Mailbox *MailboxStatus
Message *IMAPMessage
@@ -185,7 +185,7 @@ type MessageRenderData struct {
Flags map[string]bool
}

func handleGetPart(ctx *koushin.Context, raw bool) error {
func handleGetPart(ctx *alps.Context, raw bool) error {
mboxName, uid, err := parseMboxAndUid(ctx.Param("mbox"), ctx.Param("uid"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err)
@@ -271,7 +271,7 @@ func handleGetPart(ctx *koushin.Context, raw bool) error {
}

return ctx.Render(http.StatusOK, "message.html", &MessageRenderData{
BaseRenderData: *koushin.NewBaseRenderData(ctx),
BaseRenderData: *alps.NewBaseRenderData(ctx),
Mailboxes: mailboxes,
Mailbox: mbox,
Message: msg,
@@ -283,7 +283,7 @@ func handleGetPart(ctx *koushin.Context, raw bool) error {
}

type ComposeRenderData struct {
koushin.BaseRenderData
alps.BaseRenderData
Message *OutgoingMessage
}

@@ -300,12 +300,12 @@ type composeOptions struct {

// Send message, append it to the Sent mailbox, mark the original message as
// answered
func submitCompose(ctx *koushin.Context, msg *OutgoingMessage, options *composeOptions) error {
func submitCompose(ctx *alps.Context, msg *OutgoingMessage, options *composeOptions) error {
err := ctx.Session.DoSMTP(func(c *smtp.Client) error {
return sendMessage(c, msg)
})
if err != nil {
if _, ok := err.(koushin.AuthError); ok {
if _, ok := err.(alps.AuthError); ok {
return echo.NewHTTPError(http.StatusForbidden, err)
}
return fmt.Errorf("failed to send message: %v", err)
@@ -338,7 +338,7 @@ func submitCompose(ctx *koushin.Context, msg *OutgoingMessage, options *composeO
return ctx.Redirect(http.StatusFound, "/mailbox/INBOX")
}

func handleCompose(ctx *koushin.Context, msg *OutgoingMessage, options *composeOptions) error {
func handleCompose(ctx *alps.Context, msg *OutgoingMessage, options *composeOptions) error {
if msg.From == "" && strings.ContainsRune(ctx.Session.Username(), '@') {
msg.From = ctx.Session.Username()
}
@@ -438,12 +438,12 @@ func handleCompose(ctx *koushin.Context, msg *OutgoingMessage, options *composeO
}

return ctx.Render(http.StatusOK, "compose.html", &ComposeRenderData{
BaseRenderData: *koushin.NewBaseRenderData(ctx),
BaseRenderData: *alps.NewBaseRenderData(ctx),
Message: msg,
})
}

func handleComposeNew(ctx *koushin.Context) error {
func handleComposeNew(ctx *alps.Context) error {
// These are common mailto URL query parameters
// TODO: cc, bcc
return handleCompose(ctx, &OutgoingMessage{
@@ -462,7 +462,7 @@ func unwrapIMAPAddressList(addrs []*imap.Address) []string {
return l
}

func handleReply(ctx *koushin.Context) error {
func handleReply(ctx *alps.Context) error {
var inReplyToPath messagePath
var err error
inReplyToPath.Mailbox, inReplyToPath.Uid, err = parseMboxAndUid(ctx.Param("mbox"), ctx.Param("uid"))
@@ -521,7 +521,7 @@ func handleReply(ctx *koushin.Context) error {
return handleCompose(ctx, &msg, &composeOptions{InReplyTo: &inReplyToPath})
}

func handleForward(ctx *koushin.Context) error {
func handleForward(ctx *alps.Context) error {
var sourcePath messagePath
var err error
sourcePath.Mailbox, sourcePath.Uid, err = parseMboxAndUid(ctx.Param("mbox"), ctx.Param("uid"))
@@ -585,7 +585,7 @@ func handleForward(ctx *koushin.Context) error {
return handleCompose(ctx, &msg, &composeOptions{Forward: &sourcePath})
}

func handleEdit(ctx *koushin.Context) error {
func handleEdit(ctx *alps.Context) error {
var sourcePath messagePath
var err error
sourcePath.Mailbox, sourcePath.Uid, err = parseMboxAndUid(ctx.Param("mbox"), ctx.Param("uid"))
@@ -653,14 +653,14 @@ func handleEdit(ctx *koushin.Context) error {
return handleCompose(ctx, &msg, &composeOptions{Draft: &sourcePath})
}

func formOrQueryParam(ctx *koushin.Context, k string) string {
func formOrQueryParam(ctx *alps.Context, k string) string {
if v := ctx.FormValue(k); v != "" {
return v
}
return ctx.QueryParam(k)
}

func handleMove(ctx *koushin.Context) error {
func handleMove(ctx *alps.Context) error {
mboxName, err := url.PathUnescape(ctx.Param("mbox"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err)
@@ -706,7 +706,7 @@ func handleMove(ctx *koushin.Context) error {
return ctx.Redirect(http.StatusFound, fmt.Sprintf("/mailbox/%v", url.PathEscape(to)))
}

func handleDelete(ctx *koushin.Context) error {
func handleDelete(ctx *alps.Context) error {
mboxName, err := url.PathUnescape(ctx.Param("mbox"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err)
@@ -757,7 +757,7 @@ func handleDelete(ctx *koushin.Context) error {
return ctx.Redirect(http.StatusFound, fmt.Sprintf("/mailbox/%v", url.PathEscape(mboxName)))
}

func handleSetFlags(ctx *koushin.Context) error {
func handleSetFlags(ctx *alps.Context) error {
mboxName, err := url.PathUnescape(ctx.Param("mbox"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err)
@@ -840,11 +840,11 @@ type Settings struct {
MessagesPerPage int
}

func loadSettings(s koushin.Store) (*Settings, error) {
func loadSettings(s alps.Store) (*Settings, error) {
settings := &Settings{
MessagesPerPage: 50,
}
if err := s.Get(settingsKey, settings); err != nil && err != koushin.ErrNoStoreEntry {
if err := s.Get(settingsKey, settings); err != nil && err != alps.ErrNoStoreEntry {
return nil, err
}
if err := settings.check(); err != nil {
@@ -861,11 +861,11 @@ func (s *Settings) check() error {
}

type SettingsRenderData struct {
koushin.BaseRenderData
alps.BaseRenderData
Settings *Settings
}

func handleSettings(ctx *koushin.Context) error {
func handleSettings(ctx *alps.Context) error {
settings, err := loadSettings(ctx.Session.Store())
if err != nil {
return fmt.Errorf("failed to load settings: %v", err)
@@ -888,7 +888,7 @@ func handleSettings(ctx *koushin.Context) error {
}

return ctx.Render(http.StatusOK, "settings.html", &SettingsRenderData{
BaseRenderData: *koushin.NewBaseRenderData(ctx),
BaseRenderData: *alps.NewBaseRenderData(ctx),
Settings: settings,
})
}

+ 1
- 1
plugins/base/smtp.go View File

@@ -1,4 +1,4 @@
package koushinbase
package alpsbase

import (
"bufio"


+ 1
- 1
plugins/base/strconv.go View File

@@ -1,4 +1,4 @@
package koushinbase
package alpsbase

import (
"fmt"


+ 1
- 1
plugins/base/template.go View File

@@ -1,4 +1,4 @@
package koushinbase
package alpsbase

import (
"html/template"


+ 4
- 4
plugins/base/viewer.go View File

@@ -1,9 +1,9 @@
package koushinbase
package alpsbase

import (
"fmt"

"git.sr.ht/~emersion/koushin"
"git.sr.ht/~emersion/alps"
"github.com/emersion/go-message"
)

@@ -16,7 +16,7 @@ type Viewer interface {
// ViewMessagePart renders a message part. The returned value is displayed
// in a template. ErrViewUnsupported is returned if the message part isn't
// supported.
ViewMessagePart(*koushin.Context, *IMAPMessage, *message.Entity) (interface{}, error)
ViewMessagePart(*alps.Context, *IMAPMessage, *message.Entity) (interface{}, error)
}

var viewers []Viewer
@@ -26,7 +26,7 @@ func RegisterViewer(viewer Viewer) {
viewers = append(viewers, viewer)
}

func viewMessagePart(ctx *koushin.Context, msg *IMAPMessage, part *message.Entity) (interface{}, error) {
func viewMessagePart(ctx *alps.Context, msg *IMAPMessage, part *message.Entity) (interface{}, error) {
for _, viewer := range viewers {
v, err := viewer.ViewMessagePart(ctx, msg, part)
if err == ErrViewUnsupported {


+ 5
- 5
plugins/caldav/caldav.go View File

@@ -1,11 +1,11 @@
package koushincaldav
package alpscaldav

import (
"fmt"
"net/http"
"net/url"

"git.sr.ht/~emersion/koushin"
"git.sr.ht/~emersion/alps"
"github.com/emersion/go-webdav/caldav"
)

@@ -13,7 +13,7 @@ var errNoCalendar = fmt.Errorf("caldav: no calendar found")

type authRoundTripper struct {
upstream http.RoundTripper
session *koushin.Session
session *alps.Session
}

func (rt *authRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
@@ -21,7 +21,7 @@ func (rt *authRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)
return rt.upstream.RoundTrip(req)
}

func newClient(u *url.URL, session *koushin.Session) (*caldav.Client, error) {
func newClient(u *url.URL, session *alps.Session) (*caldav.Client, error) {
rt := authRoundTripper{
upstream: http.DefaultTransport,
session: session,
@@ -34,7 +34,7 @@ func newClient(u *url.URL, session *koushin.Session) (*caldav.Client, error) {
return c, nil
}

func getCalendar(u *url.URL, session *koushin.Session) (*caldav.Client, *caldav.Calendar, error) {
func getCalendar(u *url.URL, session *alps.Session) (*caldav.Client, *caldav.Calendar, error) {
c, err := newClient(u, session)
if err != nil {
return nil, nil, err


+ 7
- 7
plugins/caldav/plugin.go View File

@@ -1,11 +1,11 @@
package koushincaldav
package alpscaldav

import (
"fmt"
"net/http"
"net/url"

"git.sr.ht/~emersion/koushin"
"git.sr.ht/~emersion/alps"
)

func sanityCheckURL(u *url.URL) error {
@@ -27,9 +27,9 @@ func sanityCheckURL(u *url.URL) error {
return nil
}

func newPlugin(srv *koushin.Server) (koushin.Plugin, error) {
func newPlugin(srv *alps.Server) (alps.Plugin, error) {
u, err := srv.Upstream("caldavs", "caldav+insecure", "https", "http+insecure")
if _, ok := err.(*koushin.NoUpstreamError); ok {
if _, ok := err.(*alps.NoUpstreamError); ok {
return nil, nil
} else if err != nil {
return nil, fmt.Errorf("caldav: failed to parse upstream caldav server: %v", err)
@@ -53,7 +53,7 @@ func newPlugin(srv *koushin.Server) (koushin.Plugin, error) {

srv.Logger().Printf("Configured upstream CalDAV server: %v", u)

p := koushin.GoPlugin{Name: "caldav"}
p := alps.GoPlugin{Name: "caldav"}

registerRoutes(&p, u)

@@ -61,7 +61,7 @@ func newPlugin(srv *koushin.Server) (koushin.Plugin, error) {
}

func init() {
koushin.RegisterPluginLoader(func(s *koushin.Server) ([]koushin.Plugin, error) {
alps.RegisterPluginLoader(func(s *alps.Server) ([]alps.Plugin, error) {
p, err := newPlugin(s)
if err != nil {
return nil, err
@@ -69,6 +69,6 @@ func init() {
if p == nil {
return nil, nil
}
return []koushin.Plugin{p}, err
return []alps.Plugin{p}, err
})
}

+ 1
- 1
plugins/caldav/public/calendar.html View File

@@ -1,6 +1,6 @@
{{template "head.html"}}

<h1>koushin</h1>
<h1>alps</h1>

<p>
<a href="/">Back</a>


+ 1
- 1
plugins/caldav/public/event.html View File

@@ -1,6 +1,6 @@
{{template "head.html"}}

<h1>koushin</h1>
<h1>alps</h1>

<p>
<a href="/calendar">Back</a>


+ 9
- 9
plugins/caldav/routes.go View File

@@ -1,4 +1,4 @@
package koushincaldav
package alpscaldav

import (
"fmt"
@@ -6,12 +6,12 @@ import (
"net/url"
"time"

"git.sr.ht/~emersion/koushin"
"git.sr.ht/~emersion/alps"
"github.com/emersion/go-webdav/caldav"
)

type CalendarRenderData struct {
koushin.BaseRenderData
alps.BaseRenderData
Time time.Time
Calendar *caldav.Calendar
Events []caldav.CalendarObject
@@ -19,15 +19,15 @@ type CalendarRenderData struct {
}

type EventRenderData struct {
koushin.BaseRenderData
alps.BaseRenderData
Calendar *caldav.Calendar
Event *caldav.CalendarObject
}

var monthPageLayout = "2006-01"

func registerRoutes(p *koushin.GoPlugin, u *url.URL) {
p.GET("/calendar", func(ctx *koushin.Context) error {
func registerRoutes(p *alps.GoPlugin, u *url.URL) {
p.GET("/calendar", func(ctx *alps.Context) error {
var start time.Time
if s := ctx.QueryParam("month"); s != "" {
var err error
@@ -77,7 +77,7 @@ func registerRoutes(p *koushin.GoPlugin, u *url.URL) {
}

return ctx.Render(http.StatusOK, "calendar.html", &CalendarRenderData{
BaseRenderData: *koushin.NewBaseRenderData(ctx),
BaseRenderData: *alps.NewBaseRenderData(ctx),
Time: start,
Calendar: calendar,
Events: events,
@@ -86,7 +86,7 @@ func registerRoutes(p *koushin.GoPlugin, u *url.URL) {
})
})

p.GET("/calendar/:uid", func(ctx *koushin.Context) error {
p.GET("/calendar/:uid", func(ctx *alps.Context) error {
uid := ctx.Param("uid")

c, calendar, err := getCalendar(u, ctx.Session)
@@ -131,7 +131,7 @@ func registerRoutes(p *koushin.GoPlugin, u *url.URL) {
event := &events[0]

return ctx.Render(http.StatusOK, "event.html", &EventRenderData{
BaseRenderData: *koushin.NewBaseRenderData(ctx),
BaseRenderData: *alps.NewBaseRenderData(ctx),
Calendar: calendar,
Event: event,
})


+ 4
- 4
plugins/carddav/carddav.go View File

@@ -1,11 +1,11 @@
package koushincarddav
package alpscarddav

import (
"fmt"
"net/http"
"net/url"

"git.sr.ht/~emersion/koushin"
"git.sr.ht/~emersion/alps"
"github.com/emersion/go-webdav/carddav"
)

@@ -13,7 +13,7 @@ var errNoAddressBook = fmt.Errorf("carddav: no address book found")

type authRoundTripper struct {
upstream http.RoundTripper
session *koushin.Session
session *alps.Session
}

func (rt *authRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
@@ -21,7 +21,7 @@ func (rt *authRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)
return rt.upstream.RoundTrip(req)
}

func newClient(u *url.URL, session *koushin.Session) (*carddav.Client, error) {
func newClient(u *url.URL, session *alps.Session) (*carddav.Client, error) {
rt := authRoundTripper{
upstream: http.DefaultTransport,
session: session,


+ 13
- 13
plugins/carddav/plugin.go View File

@@ -1,4 +1,4 @@
package koushincarddav
package alpscarddav

import (
"fmt"
@@ -6,8 +6,8 @@ import (
"net/url"
"strings"

"git.sr.ht/~emersion/koushin"
koushinbase "git.sr.ht/~emersion/koushin/plugins/base"
"git.sr.ht/~emersion/alps"
alpsbase "git.sr.ht/~emersion/alps/plugins/base"
"github.com/emersion/go-vcard"
"github.com/emersion/go-webdav/carddav"
)
@@ -32,16 +32,16 @@ func sanityCheckURL(u *url.URL) error {
}

type plugin struct {
koushin.GoPlugin
alps.GoPlugin
url *url.URL
homeSetCache map[string]string
}

func (p *plugin) client(session *koushin.Session) (*carddav.Client, error) {
func (p *plugin) client(session *alps.Session) (*carddav.Client, error) {
return newClient(p.url, session)
}

func (p *plugin) clientWithAddressBook(session *koushin.Session) (*carddav.Client, *carddav.AddressBook, error) {
func (p *plugin) clientWithAddressBook(session *alps.Session) (*carddav.Client, *carddav.AddressBook, error) {
c, err := newClient(p.url, session)
if err != nil {
return nil, nil, fmt.Errorf("failed to create CardDAV client: %v", err)
@@ -73,9 +73,9 @@ func (p *plugin) clientWithAddressBook(session *koushin.Session) (*carddav.Clien
return c, &addressBooks[0], nil
}

func newPlugin(srv *koushin.Server) (koushin.Plugin, error) {
func newPlugin(srv *alps.Server) (alps.Plugin, error) {
u, err := srv.Upstream("carddavs", "carddav+insecure", "https", "http+insecure")
if _, ok := err.(*koushin.NoUpstreamError); ok {
if _, ok := err.(*alps.NoUpstreamError); ok {
return nil, nil
} else if err != nil {
return nil, fmt.Errorf("carddav: failed to parse upstream CardDAV server: %v", err)
@@ -105,7 +105,7 @@ func newPlugin(srv *koushin.Server) (koushin.Plugin, error) {
srv.Logger().Printf("Configured upstream CardDAV server: %v", u)

p := &plugin{
GoPlugin: koushin.GoPlugin{Name: "carddav"},
GoPlugin: alps.GoPlugin{Name: "carddav"},
url: u,
homeSetCache: make(map[string]string),
}
@@ -118,8 +118,8 @@ func newPlugin(srv *koushin.Server) (koushin.Plugin, error) {
},
})

p.Inject("compose.html", func(ctx *koushin.Context, _data koushin.RenderData) error {
data := _data.(*koushinbase.ComposeRenderData)
p.Inject("compose.html", func(ctx *alps.Context, _data alps.RenderData) error {
data := _data.(*alpsbase.ComposeRenderData)

c, addressBook, err := p.clientWithAddressBook(ctx.Session)
if err == errNoAddressBook {
@@ -156,7 +156,7 @@ func newPlugin(srv *koushin.Server) (koushin.Plugin, error) {
}

func init() {
koushin.RegisterPluginLoader(func(s *koushin.Server) ([]koushin.Plugin, error) {
alps.RegisterPluginLoader(func(s *alps.Server) ([]alps.Plugin, error) {
p, err := newPlugin(s)
if err != nil {
return nil, err
@@ -164,6 +164,6 @@ func init() {
if p == nil {
return nil, nil
}
return []koushin.Plugin{p}, err
return []alps.Plugin{p}, err
})
}

+ 1
- 1
plugins/carddav/public/address-book.html View File

@@ -1,6 +1,6 @@
{{template "head.html"}}

<h1>koushin</h1>
<h1>alps</h1>

<p>
<a href="/">Back</a> · <a href="/contacts/create">Create new contact</a>


+ 1
- 1
plugins/carddav/public/address-object.html View File

@@ -1,6 +1,6 @@
{{template "head.html"}}

<h1>koushin</h1>
<h1>alps</h1>

<p>
<a href="/contacts">Back</a>


+ 1
- 1
plugins/carddav/public/update-address-object.html View File

@@ -1,6 +1,6 @@
{{template "head.html"}}

<h1>koushin</h1>
<h1>alps</h1>

<p>
<a href="/contacts">Back</a>


+ 11
- 11
plugins/carddav/routes.go View File

@@ -1,4 +1,4 @@
package koushincarddav
package alpscarddav

import (
"fmt"
@@ -7,7 +7,7 @@ import (
"path"
"strings"

"git.sr.ht/~emersion/koushin"
"git.sr.ht/~emersion/alps"
"github.com/emersion/go-vcard"
"github.com/emersion/go-webdav/carddav"
"github.com/google/uuid"
@@ -15,19 +15,19 @@ import (
)

type AddressBookRenderData struct {
koushin.BaseRenderData
alps.BaseRenderData
AddressBook *carddav.AddressBook
AddressObjects []AddressObject
Query string
}

type AddressObjectRenderData struct {
koushin.BaseRenderData
alps.BaseRenderData
AddressObject AddressObject
}

type UpdateAddressObjectRenderData struct {
koushin.BaseRenderData
alps.BaseRenderData
AddressObject *carddav.AddressObject // nil if creating a new contact
Card vcard.Card
}
@@ -42,7 +42,7 @@ func parseObjectPath(s string) (string, error) {
}

func registerRoutes(p *plugin) {
p.GET("/contacts", func(ctx *koushin.Context) error {
p.GET("/contacts", func(ctx *alps.Context) error {
queryText := ctx.QueryParam("query")

c, addressBook, err := p.clientWithAddressBook(ctx.Session)
@@ -82,14 +82,14 @@ func registerRoutes(p *plugin) {
}

return ctx.Render(http.StatusOK, "address-book.html", &AddressBookRenderData{
BaseRenderData: *koushin.NewBaseRenderData(ctx),
BaseRenderData: *alps.NewBaseRenderData(ctx),
AddressBook: addressBook,
AddressObjects: newAddressObjectList(aos),
Query: queryText,
})
})

p.GET("/contacts/:path", func(ctx *koushin.Context) error {
p.GET("/contacts/:path", func(ctx *alps.Context) error {
path, err := parseObjectPath(ctx.Param("path"))
if err != nil {
return err
@@ -119,12 +119,12 @@ func registerRoutes(p *plugin) {
ao := &aos[0]

return ctx.Render(http.StatusOK, "address-object.html", &AddressObjectRenderData{
BaseRenderData: *koushin.NewBaseRenderData(ctx),
BaseRenderData: *alps.NewBaseRenderData(ctx),
AddressObject: AddressObject{ao},
})
})

updateContact := func(ctx *koushin.Context) error {
updateContact := func(ctx *alps.Context) error {
addressObjectPath, err := parseObjectPath(ctx.Param("path"))
if err != nil {
return err
@@ -200,7 +200,7 @@ func registerRoutes(p *plugin) {
}

return ctx.Render(http.StatusOK, "update-address-object.html", &UpdateAddressObjectRenderData{
BaseRenderData: *koushin.NewBaseRenderData(ctx),
BaseRenderData: *alps.NewBaseRenderData(ctx),
AddressObject: ao,
Card: card,
})


+ 9
- 9
plugins/lua/lua.go View File

@@ -1,11 +1,11 @@
package koushinlua
package alpslua

import (
"fmt"
"html/template"
"path/filepath"

"git.sr.ht/~emersion/koushin"
"git.sr.ht/~emersion/alps"
"github.com/labstack/echo/v4"
"github.com/yuin/gopher-lua"
"layeh.com/gopher-luar"
@@ -69,7 +69,7 @@ func (p *luaPlugin) setRoute(l *lua.LState) int {
return 0
}

func (p *luaPlugin) inject(name string, data koushin.RenderData) error {
func (p *luaPlugin) inject(name string, data alps.RenderData) error {
f, ok := p.renderCallbacks[name]
if !ok {
return nil
@@ -87,7 +87,7 @@ func (p *luaPlugin) inject(name string, data koushin.RenderData) error {
return nil
}

func (p *luaPlugin) Inject(ctx *koushin.Context, name string, data koushin.RenderData) error {
func (p *luaPlugin) Inject(ctx *alps.Context, name string, data alps.RenderData) error {
if err := p.inject("*", data); err != nil {
return err
}
@@ -144,8 +144,8 @@ func loadLuaPlugin(filename string) (*luaPlugin, error) {
filters: make(template.FuncMap),
}

mt := l.NewTypeMetatable("koushin")
l.SetGlobal("koushin", mt)
mt := l.NewTypeMetatable("alps")
l.SetGlobal("alps", mt)
l.SetField(mt, "on_render", l.NewFunction(p.onRender))
l.SetField(mt, "set_filter", l.NewFunction(p.setFilter))
l.SetField(mt, "set_route", l.NewFunction(p.setRoute))
@@ -158,15 +158,15 @@ func loadLuaPlugin(filename string) (*luaPlugin, error) {
return p, nil
}

func loadAllLuaPlugins(s *koushin.Server) ([]koushin.Plugin, error) {
func loadAllLuaPlugins(s *alps.Server) ([]alps.Plugin, error) {
log := s.Logger()

filenames, err := filepath.Glob(koushin.PluginDir + "/*/main.lua")
filenames, err := filepath.Glob(alps.PluginDir + "/*/main.lua")
if err != nil {
return nil, fmt.Errorf("filepath.Glob failed: %v", err)
}

plugins := make([]koushin.Plugin, 0, len(filenames))
plugins := make([]alps.Plugin, 0, len(filenames))
for _, filename := range filenames {
log.Printf("Loading Lua plugin %q", filename)



+ 3
- 3
plugins/lua/plugin.go View File

@@ -1,9 +1,9 @@
package koushinlua
package alpslua

import (
"git.sr.ht/~emersion/koushin"
"git.sr.ht/~emersion/alps"
)

func init() {
koushin.RegisterPluginLoader(loadAllLuaPlugins)
alps.RegisterPluginLoader(loadAllLuaPlugins)
}

+ 8
- 8
plugins/viewhtml/plugin.go View File

@@ -1,4 +1,4 @@
package koushinviewhtml
package alpsviewhtml

import (
"io"
@@ -8,8 +8,8 @@ import (
"strconv"
"strings"

"git.sr.ht/~emersion/koushin"
koushinbase "git.sr.ht/~emersion/koushin/plugins/base"
"git.sr.ht/~emersion/alps"
alpsbase "git.sr.ht/~emersion/alps/plugins/base"
"github.com/labstack/echo/v4"
)

@@ -19,10 +19,10 @@ var (
)

func init() {
p := koushin.GoPlugin{Name: "viewhtml"}
p := alps.GoPlugin{Name: "viewhtml"}

p.Inject("message.html", func(ctx *koushin.Context, _data koushin.RenderData) error {
data := _data.(*koushinbase.MessageRenderData)
p.Inject("message.html", func(ctx *alps.Context, _data alps.RenderData) error {
data := _data.(*alpsbase.MessageRenderData)
data.Extra["RemoteResourcesAllowed"] = ctx.QueryParam("allow-remote-resources") == "1"
hasRemoteResources := false
if v := ctx.Get("viewhtml.hasRemoteResources"); v != nil {
@@ -32,7 +32,7 @@ func init() {
return nil
})

p.GET("/proxy", func(ctx *koushin.Context) error {
p.GET("/proxy", func(ctx *alps.Context) error {
if !proxyEnabled {
return echo.NewHTTPError(http.StatusForbidden, "proxy disabled")
}
@@ -67,5 +67,5 @@ func init() {
return ctx.Stream(http.StatusOK, mediaType, &lr)
})

koushin.RegisterPluginLoader(p.Loader())
alps.RegisterPluginLoader(p.Loader())
}

+ 3
- 3
plugins/viewhtml/sanitize.go View File

@@ -1,4 +1,4 @@
package koushinviewhtml
package alpsviewhtml

import (
"bytes"
@@ -7,7 +7,7 @@ import (
"regexp"
"strings"

koushinbase "git.sr.ht/~emersion/koushin/plugins/base"
alpsbase "git.sr.ht/~emersion/alps/plugins/base"
"github.com/aymerick/douceur/css"
cssparser "github.com/chris-ramon/douceur/parser"
"github.com/microcosm-cc/bluemonday"
@@ -71,7 +71,7 @@ var allowedStyles = map[string]bool{
}

type sanitizer struct {
msg *koushinbase.IMAPMessage
msg *alpsbase.IMAPMessage
allowRemoteResources bool
hasRemoteResources bool
}


+ 6
- 6
plugins/viewhtml/viewer.go View File

@@ -1,4 +1,4 @@
package koushinviewhtml
package alpsviewhtml

import (
"bytes"
@@ -7,8 +7,8 @@ import (
"io/ioutil"
"strings"

"git.sr.ht/~emersion/koushin"
koushinbase "git.sr.ht/~emersion/koushin/plugins/base"
"git.sr.ht/~emersion/alps"
alpsbase "git.sr.ht/~emersion/alps/plugins/base"
"github.com/emersion/go-message"
)

@@ -24,7 +24,7 @@ var tpl = template.Must(template.New("view-html.html").Parse(tplSrc))

type viewer struct{}

func (viewer) ViewMessagePart(ctx *koushin.Context, msg *koushinbase.IMAPMessage, part *message.Entity) (interface{}, error) {
func (viewer) ViewMessagePart(ctx *alps.Context, msg *alpsbase.IMAPMessage, part *message.Entity) (interface{}, error) {
allowRemoteResources := ctx.QueryParam("allow-remote-resources") == "1"

mimeType, _, err := part.Header.ContentType()
@@ -32,7 +32,7 @@ func (viewer) ViewMessagePart(ctx *koushin.Context, msg *koushinbase.IMAPMessage
return nil, err
}
if !strings.EqualFold(mimeType, "text/html") {
return nil, koushinbase.ErrViewUnsupported
return nil, alpsbase.ErrViewUnsupported
}

body, err := ioutil.ReadAll(part.Body)
@@ -61,5 +61,5 @@ func (viewer) ViewMessagePart(ctx *koushin.Context, msg *koushinbase.IMAPMessage
}

func init() {
koushinbase.RegisterViewer(viewer{})
alpsbase.RegisterViewer(viewer{})
}

+ 4
- 4
plugins/viewtext/plugin.go View File

@@ -1,10 +1,10 @@
package koushinviewtext
package alpsviewtext

import (
"git.sr.ht/~emersion/koushin"
"git.sr.ht/~emersion/alps"
)

func init() {
p := koushin.GoPlugin{Name: "viewtext"}
koushin.RegisterPluginLoader(p.Loader())
p := alps.GoPlugin{Name: "viewtext"}
alps.RegisterPluginLoader(p.Loader())
}

+ 6
- 6
plugins/viewtext/viewer.go View File

@@ -1,4 +1,4 @@
package koushinviewtext
package alpsviewtext

import (
"bufio"
@@ -7,8 +7,8 @@ import (
"net/url"
"strings"

"git.sr.ht/~emersion/koushin"
koushinbase "git.sr.ht/~emersion/koushin/plugins/base"
"git.sr.ht/~emersion/alps"
alpsbase "git.sr.ht/~emersion/alps/plugins/base"
"github.com/emersion/go-message"
"gitlab.com/golang-commonmark/linkify"
)
@@ -53,13 +53,13 @@ func executeTemplate(name string, data interface{}) (template.HTML, error) {

type viewer struct{}

func (viewer) ViewMessagePart(ctx *koushin.Context, msg *koushinbase.IMAPMessage, part *message.Entity) (interface{}, error) {
func (viewer) ViewMessagePart(ctx *alps.Context, msg *alpsbase.IMAPMessage, part *message.Entity) (interface{}, error) {
mimeType, _, err := part.Header.ContentType()
if err != nil {
return nil, err
}
if !strings.EqualFold(mimeType, "text/plain") {
return nil, koushinbase.ErrViewUnsupported
return nil, alpsbase.ErrViewUnsupported
}

var tokens []interface{}
@@ -114,5 +114,5 @@ func (viewer) ViewMessagePart(ctx *koushin.Context, msg *koushinbase.IMAPMessage
}

func init() {
koushinbase.RegisterViewer(viewer{})
alpsbase.RegisterViewer(viewer{})
}

+ 2
- 2
renderer.go View File

@@ -1,4 +1,4 @@
package koushin
package alps

import (
"fmt"
@@ -60,7 +60,7 @@ type RenderData interface {
// }
//
// data := &MyRenderData{
// BaseRenderData: *koushin.NewBaseRenderData(ctx),
// BaseRenderData: *alps.NewBaseRenderData(ctx),
// // other fields...
// }
func NewBaseRenderData(ctx *Context) *BaseRenderData {


+ 4
- 4
server.go View File

@@ -1,4 +1,4 @@
package koushin
package alps

import (
"fmt"
@@ -11,9 +11,9 @@ import (
"github.com/labstack/echo/v4"
)

const cookieName = "koushin_session"
const cookieName = "alps_session"

// Server holds all the koushin server state.
// Server holds all the alps server state.
type Server struct {
e *echo.Echo
Sessions *SessionManager
@@ -237,7 +237,7 @@ func (s *Server) Logger() echo.Logger {
//
// Use a type assertion to get it from a echo.Context:
//
// ctx := ectx.(*koushin.Context)
// ctx := ectx.(*alps.Context)
type Context struct {
echo.Context
Server *Server


+ 1
- 1
session.go View File

@@ -1,4 +1,4 @@
package koushin
package alps

import (
"crypto/rand"


+ 1
- 1
smtp.go View File

@@ -1,4 +1,4 @@
package koushin
package alps

import (
"fmt"


+ 9
- 9
store.go View File

@@ -1,4 +1,4 @@
package koushin
package alps

import (
"encoding/json"
@@ -12,7 +12,7 @@ import (
)

// ErrNoStoreEntry is returned by Store.Get when the entry doesn't exist.
var ErrNoStoreEntry = fmt.Errorf("koushin: no such entry in store")
var ErrNoStoreEntry = fmt.Errorf("alps: no such entry in store")

// Store allows storing per-user persistent data.
//
@@ -72,14 +72,14 @@ type imapStore struct {
cache *memoryStore
}

var errIMAPMetadataUnsupported = fmt.Errorf("koushin: IMAP server doesn't support METADATA extension")
var errIMAPMetadataUnsupported = fmt.Errorf("alps: IMAP server doesn't support METADATA extension")

func newIMAPStore(session *Session) (*imapStore, error) {
err := session.DoIMAP(func(c *imapclient.Client) error {
mc := imapmetadata.NewClient(c)
ok, err := mc.SupportMetadata()
if err != nil {
return fmt.Errorf("koushin: failed to check for IMAP METADATA support: %v", err)
return fmt.Errorf("alps: failed to check for IMAP METADATA support: %v", err)
}
if !ok {
return errIMAPMetadataUnsupported
@@ -93,7 +93,7 @@ func newIMAPStore(session *Session) (*imapStore, error) {
}

func (s *imapStore) key(key string) string {
return "/private/vendor/koushin/" + key
return "/private/vendor/alps/" + key
}

func (s *imapStore) Get(key string, out interface{}) error {
@@ -109,14 +109,14 @@ func (s *imapStore) Get(key string, out interface{}) error {
return err
})
if err != nil {
return fmt.Errorf("koushin: failed to fetch IMAP store entry %q: %v", key, err)
return fmt.Errorf("alps: failed to fetch IMAP store entry %q: %v", key, err)
}
v, ok := entries[s.key(key)]
if !ok {
return ErrNoStoreEntry
}
if err := json.Unmarshal([]byte(v), out); err != nil {
return fmt.Errorf("koushin: failed to unmarshal IMAP store entry %q: %v", key, err)
return fmt.Errorf("alps: failed to unmarshal IMAP store entry %q: %v", key, err)
}
return s.cache.Put(key, out)
}
@@ -124,7 +124,7 @@ func (s *imapStore) Get(key string, out interface{}) error {
func (s *imapStore) Put(key string, v interface{}) error {
b, err := json.Marshal(v)
if err != nil {
return fmt.Errorf("koushin: failed to marshal IMAP store entry %q: %v", key, err)
return fmt.Errorf("alps: failed to marshal IMAP store entry %q: %v", key, err)
}
entries := map[string]string{
s.key(key): string(b),
@@ -134,7 +134,7 @@ func (s *imapStore) Put(key string, v interface{}) error {
return mc.SetMetadata("", entries)
})
if err != nil {
return fmt.Errorf("koushin: failed to put IMAP store entry %q: %v", key, err)
return fmt.Errorf("alps: failed to put IMAP store entry %q: %v", key, err)
}

return s.cache.Put(key, v)


+ 1
- 1
themes/alps/login.html View File

@@ -1,5 +1,5 @@
{{template "head.html"}}
<h1>koushin webmail</h1>
<h1>alps webmail</h1>

<form method="post" action="/login">
<p>


+ 1
- 1
themes/sourcehut/head.html View File

@@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<title>koushin webmail</title>
<title>alps webmail</title>
<link rel="stylesheet" href="/themes/sourcehut/assets/style.css">
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4QIGCC8n92KyhQAAAj1QTFRFAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////anIwUQAAAL50Uk5TAAECAwQFBgcICQoLDA4PEBESExQVFhcYGRobHB0eHyAhIyQmJygpKistLzAzNDU2Nzg5Ozw9QEFDREZHSElLTE1OT1BRVFdYWVpbXF1eX2BhZGZnaGltbnBxdHV3eHp7fn+AgYKDhIWGh4iJio2TlJucnqGio6Smp6ipqqusrbCxsrO0tre4ury9vr/Cw8TFxsfIycrMzc7P0dLT1dbY2dvf4OLj5OXm5+jq6+zt7u/w8fL09fb3+Pn6+/z9/gNzyOkAAAABYktHRL6k3IPDAAAFwUlEQVQYGe3B+VtUVQAG4G9i0TQZZyA1S0JxydzDNFTUqXBfcylzS8UE21TMyjAQUQnFEi0BHQU3cAc0UGbm+9v65Zw7y70zc++dc3qenof3xZAhQ4b8T+V/uGn/kdrm1psdHTdbm2uP7Ns434//yLD5e+u7aOH+6T0ludBszGf1L5jC87rNBdDm9XXnw0wrfG71cOhQ9N0z2vTk20Ko9t5PYToQOj4FKr17IkKHwtUToMobVQN0of/rkVDik7t06c5yZC7/V1rqajq6f9vKQFlZYNW2A0cvdNPSL35kaPEDmgxerFiSjwQFZQebQzTpKkUmsg4xUX/Nijwk4V15aoAJIhVZcM13ngnatuQhJe/WNiY464VLE28xXsMCD9LyLDzLeMEiuDLzIeM0zIRNs88xTvcMuFDSx1htC+FA6XXG6pkLx0qeM8aLHdlwJGfnP4zRNw8OzexjjOaJcGzSn4zR8z4cmfiQUZF9WXAhuyLCqO4iOOC7xahni+BSWQ+jgl7YlnWeUR3FcG1qJ6MasmDXIUZdexMZGNvGqAOwaTGjrnqREd/fNERKYUvBAxqueZEhXzsNXX7Y8SsNHW8iY+M6afgZNnxKw7NiKDCtl4YA0nrjHqXIIiixjIY7I5FOFQ37oEglDZVIo+glpeYsKJJ9mdJAIVI7QenFRChT3E+pGim9F6G0AwrtohSeglR+otSWDYVygpSqkUJRmNJCKLWEUmgCkvuOUgPU8vxO6TCSev0ZpZlQ7ANKj4YhmXWUGqBcI6UVSOY8pQVQbhGlM0hiTJhCmwfKeW5QCPlh7TNKW6DBdkobYa2eQn8eNPC9pPAbLA17QaEGWtRS6MuBlfmUVkCLtZTmwspeCoN50MIXprATVuopXIQmLRROwUoXhQpoUkXhNizkU1oCTZZRyoPZh5Tyock4SnNgtolCF3TxPKKwBmb7KTRBm0sUdsPsCIWj0OY4hR9gVkthP7SppHASZs0UtkGbLyg0wqyVwkpos4FCC8xuUghAm3IK7TDroFAGbQIUgjDroFAGbQIUgjC7SSEAbcoptMOslcIqaLOBQgvMmilsgzY7KDTCrJbCAWhTReEkzI5QOAptfqTwPcz2UbgAbS5R2AWzjRS6oYvnMYXVMJtPqQCavEVpNsz8lMqgyXJKo2DhPoWD0OQwhU5YOU2hGZpcoVADK3sohLzQwh+h8CWslFBaCS3WUZoDK7nPKZyCFnUUerNh6TSFAS808L+iUANrmylthQafU1oPawVhCm0eKOcJUhgcjSTOUVoI5RZTqkMyqymdhXJNlMqRzPAnlGZDsRJKD3OR1LeUzkEtTxOlSiRXGKL0EZRaSmlwPFI4Tul6DhTKvUXpGFKZEqa0EwrtoRQqRkrVlP6ZBGUmD1A6htQm9FP6IxuK5Fyl1P8O0viahgoo8g0NB5HOyDuUImVQ4mMabo9AWgEaeqZCgel9NCyFDb/Q0DkWGXv7Lg0nYIe/i4Y2HzKUf4OGe6NhS2mEhr98yEh+Kw3hBbCpglHt45CB8TcY9RXsyjrLqM5pcG36XUadfg22eYOM6l0Glz7uY9T1UXCg6AFjHMqGCznfMMb9Qjgyo4cxLhfDsclXGePpdDg0r48x+nflwJHcPQOM0TsLjs3rYazgEtjnWXqLsZ7Oggszuhnn9w9gU0kT49yfDleKgozXuMiDtDyLmxjveiFc8jYwwY3tPqTk/zzIBPWj4FpWRYQJXtau9SEJ/7q6V0wQ/uo1ZKK0iybhlqplYxHP89byw1ciNLm3ABny/0xLjy4dr/xiQ3kgUL5hR9WPlx7T0onRyFzgDl26vRRKjKwcoAv9B0dAlcLqMB0KH3sHKk2pDtGBwWPFUG3C4Ue06WHleOgwbMWZENMarCvPhTb+jb/1MYXemvWjoVnO3J2nbtNCZ82Xc7LxH8mbs2b3DycbW9qDwfaWxpPf71o9exSGDBky5P/pX9F6dsCMuJp+AAAAAElFTkSuQmCC" />
</head>


+ 1
- 1
themes/sourcehut/nav.html View File

@@ -1,7 +1,7 @@
<nav class="container-fluid navbar navbar-light navbar-expand-sm">
<!-- TODO: show active plugin name -->
<a class="navbar-brand" href="/">
koushin
alps
<span class="text-danger">mail</span>
</a>
{{if .LoggedIn}}


Loading…
Cancel
Save