Receive events, fixes #8

This commit is contained in:
emersion 2017-09-09 18:23:14 +02:00
parent 907e88f759
commit d33218dc7c
No known key found for this signature in database
GPG Key ID: 0FDE7BE0E88F5E48
3 changed files with 149 additions and 14 deletions

View File

@ -4,6 +4,7 @@ import (
"net/http"
"os"
"strings"
"sync"
"time"
"github.com/emersion/hydroxide/protonmail"
@ -86,6 +87,7 @@ func (ao *addressObject) Stat() (os.FileInfo, error) {
type addressBook struct {
c *protonmail.Client
cache map[string]*addressObject
locker sync.Mutex
total int
}
@ -98,15 +100,34 @@ func (ab *addressBook) Info() (*carddav.AddressBookInfo, error) {
}
func (ab *addressBook) cacheComplete() bool {
ab.locker.Lock()
defer ab.locker.Unlock()
return ab.total >= 0 && len(ab.cache) == ab.total
}
func (ab *addressBook) addressObject(id string) (*addressObject, bool) {
ab.locker.Lock()
defer ab.locker.Unlock()
ao, ok := ab.cache[id]
return ao, ok
}
func (ab *addressBook) cacheAddressObject(ao *addressObject) {
ab.locker.Lock()
defer ab.locker.Unlock()
ab.cache[ao.contact.ID] = ao
}
func (ab *addressBook) ListAddressObjects() ([]carddav.AddressObject, error) {
if ab.cacheComplete() {
ab.locker.Lock()
defer ab.locker.Unlock()
aos := make([]carddav.AddressObject, 0, len(ab.cache))
for _, ao := range ab.cache {
aos = append(aos, ao)
}
return aos, nil
}
@ -116,14 +137,16 @@ func (ab *addressBook) ListAddressObjects() ([]carddav.AddressObject, error) {
if err != nil {
return nil, err
}
ab.locker.Lock()
ab.total = total
ab.locker.Unlock()
for _, contact := range contacts {
if _, ok := ab.cache[contact.ID]; !ok {
ab.cache[contact.ID] = &addressObject{
if _, ok := ab.addressObject(contact.ID); !ok {
ab.cacheAddressObject(&addressObject{
c: ab.c,
contact: contact,
}
})
}
}
@ -131,31 +154,30 @@ func (ab *addressBook) ListAddressObjects() ([]carddav.AddressObject, error) {
var aos []carddav.AddressObject
page := 0
for {
total, contacts, err := ab.c.ListContactsExport(page, 0)
_, contacts, err := ab.c.ListContactsExport(page, 0)
if err != nil {
return nil, err
}
ab.total = total
if aos == nil {
aos = make([]carddav.AddressObject, 0, total)
}
for _, contact := range contacts {
ao, ok := ab.cache[contact.ID]
ao, ok := ab.addressObject(contact.ID)
if !ok {
ao = &addressObject{
c: ab.c,
contact: &protonmail.Contact{ID: contact.ID},
}
ab.cache[contact.ID] = ao
ab.cacheAddressObject(ao)
}
ao.contact.Cards = contact.Cards
aos = append(aos, ao)
}
if len(aos) == total || len(contacts) == 0 {
if len(aos) >= total || len(contacts) == 0 {
break
}
page++
@ -165,7 +187,7 @@ func (ab *addressBook) ListAddressObjects() ([]carddav.AddressObject, error) {
}
func (ab *addressBook) GetAddressObject(id string) (carddav.AddressObject, error) {
if ao, ok := ab.cache[id]; ok {
if ao, ok := ab.addressObject(id); ok {
return ao, nil
} else if ab.cacheComplete() {
return nil, carddav.ErrNotFound
@ -180,14 +202,47 @@ func (ab *addressBook) GetAddressObject(id string) (carddav.AddressObject, error
c: ab.c,
contact: contact,
}
ab.cache[id] = ao
ab.cacheAddressObject(ao)
return ao, nil
}
func NewHandler(c *protonmail.Client) http.Handler {
return carddav.NewHandler(&addressBook{
func (ab *addressBook) receiveEvents(events <-chan *protonmail.Event) {
for event := range events {
ab.locker.Lock()
if event.Refresh == 1 {
ab.cache = make(map[string]*addressObject)
ab.total = -1
} else if len(event.Contacts) > 0 {
for _, eventContact := range event.Contacts {
switch eventContact.Action {
case protonmail.EventCreate:
ab.total++
fallthrough
case protonmail.EventUpdate:
ab.cache[eventContact.ID] = &addressObject{
c: ab.c,
contact: eventContact.Contact,
}
case protonmail.EventDelete:
delete(ab.cache, eventContact.ID)
ab.total--
}
}
}
ab.locker.Unlock()
}
}
func NewHandler(c *protonmail.Client, events <-chan *protonmail.Event) http.Handler {
ab := &addressBook{
c: c,
cache: make(map[string]*addressObject),
total: -1,
})
}
if events != nil {
go ab.receiveEvents(events)
}
return carddav.NewHandler(ab)
}

View File

@ -12,6 +12,7 @@ import (
"log"
"net/http"
"os"
"time"
"github.com/emersion/hydroxide/carddav"
"github.com/emersion/hydroxide/protonmail"
@ -122,6 +123,26 @@ func authenticate(c *protonmail.Client, cachedAuth *cachedAuth) error {
return err
}
func receiveEvents(c *protonmail.Client, last string, ch chan<- *protonmail.Event) {
t := time.NewTicker(time.Minute)
defer t.Stop()
for range t.C {
event, err := c.GetEvent(last)
if err != nil {
log.Println("Cannot receive event:", err)
continue
}
if event.ID == last {
continue
}
last = event.ID
ch <- event
}
}
type session struct {
h http.Handler
hashedSecretKey []byte
@ -286,7 +307,9 @@ func main() {
return
}
h = carddav.NewHandler(c)
events := make(chan *protonmail.Event)
go receiveEvents(c, cachedAuth.EventID, events)
h = carddav.NewHandler(c, events)
sessions[username] = &session{
h: h,

57
protonmail/events.go Normal file
View File

@ -0,0 +1,57 @@
package protonmail
import (
"net/http"
)
type Event struct {
ID string `json:"EventID"`
Refresh int
//Messages
Contacts []*EventContact
//ContactEmails
//Labels
//User
//Members
//Domains
//Organization
//MessageCounts
//ConversationCounts
//UsedSpace
Notices []string
}
type EventAction int
const (
EventDelete EventAction = iota
EventCreate
EventUpdate
)
type EventContact struct {
ID string
Action EventAction
Contact *Contact
}
func (c *Client) GetEvent(last string) (*Event, error) {
if last == "" {
last = "latest"
}
req, err := c.newRequest(http.MethodGet, "/events/"+last, nil)
if err != nil {
return nil, err
}
var respData struct {
resp
*Event
}
if err := c.doJSON(req, &respData); err != nil {
return nil, err
}
return respData.Event, nil
}