diff --git a/imessage/interface.go b/imessage/interface.go index 423ab6bd..1af22deb 100644 --- a/imessage/interface.go +++ b/imessage/interface.go @@ -65,7 +65,7 @@ type API interface { PrepareDM(guid string) error SendMessage(chatID, text string, replyTo string, replyToPart int, richLink *RichLink, metadata MessageMetadata) (*SendResponse, error) - SendFile(chatID, text, filename string, pathOnDisk string, replyTo string, replyToPart int, mimeType string, voiceMemo bool, metadata MessageMetadata) (*SendResponse, error) + SendFile(chatID, text, filename string, pathOnDisk, guid string, replyTo string, replyToPart int, mimeType string, voiceMemo bool, metadata MessageMetadata) (*SendResponse, error) SendFileCleanup(sendFileDir string) SendTapback(chatID, targetGUID string, targetPart int, tapback TapbackType, remove bool) (*SendResponse, error) SendReadReceipt(chatID, readUpTo string) error @@ -81,6 +81,11 @@ type API interface { Capabilities() ConnectorCapabilities } +type FilePreparingAPI interface { + API + SendFilePrepare(filename string, data []byte) (dir, guid string, err error) +} + var TempFilePermissions os.FileMode = 0640 var TempDirPermissions os.FileMode = 0700 diff --git a/imessage/ios/ipc.go b/imessage/ios/ipc.go index 8381fd64..7c5faa4b 100644 --- a/imessage/ios/ipc.go +++ b/imessage/ios/ipc.go @@ -71,6 +71,7 @@ func timeToFloat(time time.Time) float64 { type APIWithIPC interface { imessage.API SetIPC(*ipc.Processor) + GetIPC() *ipc.Processor SetContactProxy(api imessage.ContactAPI) SetChatInfoProxy(api imessage.ChatInfoAPI) } @@ -121,6 +122,10 @@ func (ios *iOSConnector) SetIPC(proc *ipc.Processor) { ios.IPC = proc } +func (ios *iOSConnector) GetIPC() *ipc.Processor { + return ios.IPC +} + func (ios *iOSConnector) SetContactProxy(api imessage.ContactAPI) { ios.contactProxy = api } @@ -517,12 +522,13 @@ func (ios *iOSConnector) SendMessage(chatID, text string, replyTo string, replyT return &resp, err } -func (ios *iOSConnector) SendFile(chatID, text, filename string, pathOnDisk string, replyTo string, replyToPart int, mimeType string, voiceMemo bool, metadata imessage.MessageMetadata) (*imessage.SendResponse, error) { +func (ios *iOSConnector) SendFile(chatID, text, filename, pathOnDisk, guid, replyTo string, replyToPart int, mimeType string, voiceMemo bool, metadata imessage.MessageMetadata) (*imessage.SendResponse, error) { var resp imessage.SendResponse err := ios.IPC.Request(context.Background(), ReqSendMedia, &SendMediaRequest{ ChatGUID: chatID, Text: text, Attachment: imessage.Attachment{ + GUID: guid, FileName: filename, PathOnDisk: pathOnDisk, MimeType: mimeType, diff --git a/imessage/ios/requests.go b/imessage/ios/requests.go index e095e426..da0ea78c 100644 --- a/imessage/ios/requests.go +++ b/imessage/ios/requests.go @@ -25,6 +25,7 @@ import ( const ( ReqSendMessage ipc.Command = "send_message" + ReqUploadMedia ipc.Command = "upload_media" ReqSendMedia ipc.Command = "send_media" ReqSendTapback ipc.Command = "send_tapback" ReqSendReadReceipt ipc.Command = "send_read_receipt" @@ -66,6 +67,14 @@ type SendMediaRequest struct { Metadata imessage.MessageMetadata `json:"metadata,omitempty"` } +type UploadMediaRequest struct { + PathOnDisk string `json:"path_on_disk"` +} + +type UploadMediaResponse struct { + GUID string `json:"guid"` +} + type SendTapbackRequest struct { ChatGUID string `json:"chat_guid"` TargetGUID string `json:"target_guid"` diff --git a/imessage/mac-nosip/nosip.go b/imessage/mac-nosip/nosip.go index 0b92325e..79d28767 100644 --- a/imessage/mac-nosip/nosip.go +++ b/imessage/mac-nosip/nosip.go @@ -17,6 +17,7 @@ package mac_nosip import ( + "context" "encoding/json" "errors" "fmt" @@ -320,6 +321,22 @@ func (mac *MacNoSIPConnector) Capabilities() imessage.ConnectorCapabilities { } } +func (mac *MacNoSIPConnector) SendFilePrepare(filename string, data []byte) (string, string, error) { + dir, path, err := imessage.SendFilePrepare(filename, data) + if err != nil { + return dir, "", fmt.Errorf("failed to write file to disk: %w", err) + } + var resp ios.UploadMediaResponse + ctx := context.Background() + err = mac.GetIPC().Request(ctx, ios.ReqUploadMedia, &ios.UploadMediaRequest{ + PathOnDisk: path, + }, &resp) + if err != nil { + return dir, "", fmt.Errorf("failed to upload file: %w", err) + } + return dir, resp.GUID, nil +} + func init() { imessage.Implementations["mac-nosip"] = NewMacNoSIPConnector } diff --git a/imessage/mac/send.go b/imessage/mac/send.go index 5c634f0d..5326c13f 100644 --- a/imessage/mac/send.go +++ b/imessage/mac/send.go @@ -182,7 +182,7 @@ func (mac *macOSDatabase) SendMessage(chatID, text string, replyTo string, reply return nil, mac.sendMessageWithRetry(sendMessage, sendMessageWithService, sendMessageBuddy, imessage.ParseIdentifier(chatID), text) } -func (mac *macOSDatabase) SendFile(chatID, text, filename string, pathOnDisk string, replyTo string, replyToPart int, mimeType string, voiceMemo bool, metadata imessage.MessageMetadata) (*imessage.SendResponse, error) { +func (mac *macOSDatabase) SendFile(chatID, text, filename, pathOnDisk, guid, replyTo string, replyToPart int, mimeType string, voiceMemo bool, metadata imessage.MessageMetadata) (*imessage.SendResponse, error) { if voiceMemo { mac.log.Warn("received a request to send a file as a voice memo, but mac does not support this. sending as regular attachment.") } diff --git a/portal.go b/portal.go index 60105666..3adcfda8 100644 --- a/portal.go +++ b/portal.go @@ -1313,8 +1313,13 @@ func (portal *Portal) handleMatrixMediaDirect(url id.ContentURI, file *event.Enc } } - var dir, filePath string - dir, filePath, err = imessage.SendFilePrepare(filename, data) + var dir, filePath, guid string + prep, ok := portal.bridge.IM.(imessage.FilePreparingAPI) + if ok { + dir, guid, err = prep.SendFilePrepare(filename, data) + } else { + dir, filePath, err = imessage.SendFilePrepare(filename, data) + } if err != nil { portal.log.Errorfln("failed to prepare to send file: %w", err) return @@ -1333,7 +1338,7 @@ func (portal *Portal) handleMatrixMediaDirect(url id.ContentURI, file *event.Enc } } - resp, err = portal.bridge.IM.SendFile(portal.getTargetGUID("media message", evt.ID, ""), caption, filename, filePath, messageReplyID, messageReplyPart, mimeType, isVoiceMemo, metadata) + resp, err = portal.bridge.IM.SendFile(portal.getTargetGUID("media message", evt.ID, ""), caption, filename, filePath, guid, messageReplyID, messageReplyPart, mimeType, isVoiceMemo, metadata) portal.bridge.IM.SendFileCleanup(dir) return }