95 lines
3.2 KiB
Go
95 lines
3.2 KiB
Go
package jmap
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strconv"
|
|
)
|
|
|
|
// sessionCapability is one entry under the Session object's "capabilities" (account-
|
|
// independent) or an account's own "accountCapabilities" — both are just
|
|
// urn -> settings-object maps, an empty object being valid when a capability has no
|
|
// extra settings to advertise (RFC 8620 §2).
|
|
type sessionCapability = map[string]any
|
|
|
|
type sessionAccount struct {
|
|
Name string `json:"name"`
|
|
IsPersonal bool `json:"isPersonal"`
|
|
IsReadOnly bool `json:"isReadOnly"`
|
|
AccountCapabilities map[string]sessionCapability `json:"accountCapabilities"`
|
|
}
|
|
|
|
type sessionResponse struct {
|
|
Capabilities map[string]sessionCapability `json:"capabilities"`
|
|
Accounts map[string]sessionAccount `json:"accounts"`
|
|
PrimaryAccounts map[string]string `json:"primaryAccounts"`
|
|
Username string `json:"username"`
|
|
APIURL string `json:"apiUrl"`
|
|
DownloadURL string `json:"downloadUrl"`
|
|
UploadURL string `json:"uploadUrl"`
|
|
EventSourceURL string `json:"eventSourceUrl"`
|
|
State string `json:"state"`
|
|
}
|
|
|
|
const (
|
|
capCore = "urn:ietf:params:jmap:core"
|
|
capMail = "urn:ietf:params:jmap:mail"
|
|
capSubmission = "urn:ietf:params:jmap:submission"
|
|
)
|
|
|
|
// handleSession serves the RFC 8620 §2 Session resource — the entry point a JMAP
|
|
// client fetches once (and re-fetches on the Basic realm challenge/whenever the
|
|
// server's "state" changes) to discover every other endpoint URL and this account's
|
|
// capabilities.
|
|
func (b *Backend) handleSession(w http.ResponseWriter, r *http.Request) {
|
|
mbox := mailboxFromContext(r)
|
|
accountID := strconv.FormatInt(mbox.ID, 10)
|
|
|
|
maxSize := b.Cfg.Section("Mailstore").Key("max_message_bytes").MustInt64(25 * 1024 * 1024)
|
|
|
|
resp := sessionResponse{
|
|
Capabilities: map[string]sessionCapability{
|
|
capCore: {
|
|
"maxSizeUpload": maxSize,
|
|
"maxConcurrentUpload": 4,
|
|
"maxSizeRequest": maxSize,
|
|
"maxConcurrentRequests": 4,
|
|
"maxCallsInRequest": 32,
|
|
"maxObjectsInGet": 500,
|
|
"maxObjectsInSet": 500,
|
|
"collationAlgorithms": []string{},
|
|
},
|
|
capMail: {},
|
|
capSubmission: {},
|
|
},
|
|
Accounts: map[string]sessionAccount{
|
|
accountID: {
|
|
Name: mbox.Email,
|
|
IsPersonal: true,
|
|
IsReadOnly: false,
|
|
AccountCapabilities: map[string]sessionCapability{
|
|
capCore: {}, capMail: {}, capSubmission: {},
|
|
},
|
|
},
|
|
},
|
|
PrimaryAccounts: map[string]string{
|
|
capMail: accountID,
|
|
capSubmission: accountID,
|
|
},
|
|
Username: mbox.Email,
|
|
APIURL: "/jmap/api",
|
|
DownloadURL: "/jmap/download/{accountId}/{blobId}/{name}?type={type}",
|
|
UploadURL: "/jmap/upload/{accountId}",
|
|
EventSourceURL: "/jmap/eventsource?types={types}&closeafter={closeafter}&ping={ping}",
|
|
}
|
|
state, err := b.DB.MessagesState(mbox.ID)
|
|
if err != nil {
|
|
http.Error(w, "internal error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
resp.State = state
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(resp)
|
|
}
|