diff --git a/protocol/persistence.go b/protocol/persistence.go index 14ee3c699..7aad1df66 100644 --- a/protocol/persistence.go +++ b/protocol/persistence.go @@ -545,6 +545,7 @@ func (db sqlitePersistence) Contacts() ([]*Contact, error) { c.contact_request_clock, i.image_type, i.payload, + i.clock, COALESCE(c.verification_status, 0) as verification_status, COALESCE(t.trust_status, 0) as trust_status FROM contacts c @@ -573,6 +574,7 @@ func (db sqlitePersistence) Contacts() ([]*Contact, error) { removed sql.NullBool hasAddedUs sql.NullBool lastUpdatedLocally sql.NullInt64 + identityImageClock sql.NullInt64 imagePayload []byte ) @@ -597,6 +599,7 @@ func (db sqlitePersistence) Contacts() ([]*Contact, error) { &contactRequestClock, &imageType, &imagePayload, + &identityImageClock, &contact.VerificationStatus, &contact.TrustStatus, ) @@ -651,13 +654,13 @@ func (db sqlitePersistence) Contacts() ([]*Contact, error) { previousContact, ok := allContacts[contact.ID] if !ok { if imageType.Valid { - contact.Images[imageType.String] = images.IdentityImage{Name: imageType.String, Payload: imagePayload} + contact.Images[imageType.String] = images.IdentityImage{Name: imageType.String, Payload: imagePayload, Clock: uint64(identityImageClock.Int64)} } allContacts[contact.ID] = &contact } else if imageType.Valid { - previousContact.Images[imageType.String] = images.IdentityImage{Name: imageType.String, Payload: imagePayload} + previousContact.Images[imageType.String] = images.IdentityImage{Name: imageType.String, Payload: imagePayload, Clock: uint64(identityImageClock.Int64)} allContacts[contact.ID] = previousContact } diff --git a/server/handlers.go b/server/handlers.go index cb6c54f82..aa6ea66d1 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -13,6 +13,7 @@ import ( "github.com/status-im/status-go/ipfs" "github.com/status-im/status-go/protocol/common" + identityImages "github.com/status-im/status-go/images" "github.com/status-im/status-go/protocol/identity/identicon" "github.com/status-im/status-go/protocol/images" "github.com/status-im/status-go/signal" @@ -23,6 +24,7 @@ const ( identiconsPath = basePath + "/identicons" imagesPath = basePath + "/images" audioPath = basePath + "/audio" + identityImagesPath = "/identityImages" ipfsPath = "/ipfs" // Handler routes for pairing @@ -62,6 +64,81 @@ func handleIdenticon(logger *zap.Logger) http.HandlerFunc { } } +// TODO: return error +func handleIdentityImage(db *sql.DB, logger *zap.Logger) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + pks, ok := r.URL.Query()["publicKey"] + if !ok || len(pks) == 0 { + logger.Error("no publicKey") + return + } + pk := pks[0] + rows, err := db.Query(`SELECT image_type, payload FROM chat_identity_contacts WHERE contact_id = ?`, pk) + if err != nil { + logger.Error("could not fetch identity images") + return + } + + defer rows.Close() + + var identityImage identityImages.IdentityImage + for rows.Next() { + var imageType sql.NullString + var imagePayload []byte + err := rows.Scan( + &imageType, + &imagePayload, + ) + if err != nil { + logger.Error("could not scan image row") + return + } + + identityImage = identityImages.IdentityImage{Name: imageType.String, Payload: imagePayload} + } + + imageType := "image/png" + var image []byte + + + if identityImage.Payload != nil { + t, err := identityImage.GetType() + if err != nil { + logger.Error("could not get image type") + return + } + switch t { + case identityImages.JPEG: + imageType = "image/jpeg" + case identityImages.PNG: + imageType = "image/png" + case identityImages.GIF: + imageType = "image/gif" + case identityImages.WEBP: + imageType = "image/webp" + } + image = identityImage.Payload + } else { + + image, err = identicon.Generate(pk) + if err != nil { + logger.Error("could not generate identicon") + return + } + } + + + w.Header().Set("Content-Type", imageType) + w.Header().Set("Cache-Control", "max-age:290304000, public") + w.Header().Set("Expires", time.Now().AddDate(60, 0, 0).Format(http.TimeFormat)) + + _, err = w.Write(image) + if err != nil { + logger.Error("failed to write image", zap.Error(err)) + } + } +} + func handleImage(db *sql.DB, logger *zap.Logger) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { messageIDs, ok := r.URL.Query()["messageId"] diff --git a/server/server.go b/server/server.go index dafd013c0..090f5a211 100644 --- a/server/server.go +++ b/server/server.go @@ -11,6 +11,7 @@ import ( "go.uber.org/zap" "github.com/status-im/status-go/logutils" + "github.com/status-im/status-go/signal" ) type Server struct { @@ -48,6 +49,7 @@ func (s *Server) listenAndServe() { } s.port = listener.Addr().(*net.TCPAddr).Port + signal.SendMediaServerStarted(s.port) s.run = true err = s.server.Serve(listener) diff --git a/server/server_media.go b/server/server_media.go index 5e9c99b05..e1b50a500 100644 --- a/server/server_media.go +++ b/server/server_media.go @@ -29,6 +29,7 @@ func NewMediaServer(db *sql.DB, downloader *ipfs.Downloader) (*MediaServer, erro s.SetHandlers(HandlerPatternMap{ imagesPath: handleImage(s.db, s.logger), audioPath: handleAudio(s.db, s.logger), + identityImagesPath: handleIdentityImage(s.db, s.logger), identiconsPath: handleIdenticon(s.logger), ipfsPath: handleIPFS(s.downloader, s.logger), }) diff --git a/signal/events_messenger.go b/signal/events_messenger.go index f3cba2dbc..1b79c3713 100644 --- a/signal/events_messenger.go +++ b/signal/events_messenger.go @@ -1,6 +1,9 @@ package signal const ( + // EventMediaServerStarted triggers when the media server successfully binds a new port + EventMediaServerStarted = "mediaserver.started" + // EventMesssageDelivered triggered when we got acknowledge from datasync level, that means peer got message EventMesssageDelivered = "message.delivered" @@ -19,6 +22,12 @@ type MessageDeliveredSignal struct { MessageID string `json:"messageID"` } +// MediaServerStarted specifies chat and message that was delivered +type MediaServerStarted struct { + Port int `json:"port"` +} + +// MessageDeliveredSignal specifies chat and message that was delivered type CommunityInfoFoundSignal struct { Name string `json:"name"` Description string `json:"description"` @@ -31,6 +40,12 @@ func SendMessageDelivered(chatID string, messageID string) { send(EventMesssageDelivered, MessageDeliveredSignal{ChatID: chatID, MessageID: messageID}) } +// SendMediaServerStarted notifies about restarts of the media server +func SendMediaServerStarted(port int) { + send(EventMediaServerStarted, MediaServerStarted{Port: port}) +} + + // SendMessageDelivered notifies about delivered message func SendCommunityInfoFound(community interface{}) { send(EventCommunityInfoFound, community)