// Copyright 2014 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only

// Source: https://github.com/signalapp/libsignal-service-java/blob/4684a49b2ed8f32be619e0d0eea423626b6cb2cb/protobuf/SignalService.proto
package signalservice;

option java_package = "org.whispersystems.signalservice.internal.push";
option java_outer_classname = "SignalServiceProtos";

message Envelope {
  enum Type {
    UNKNOWN             = 0;
    CIPHERTEXT          = 1;
    KEY_EXCHANGE        = 2;
    PREKEY_BUNDLE       = 3;
    RECEIPT             = 5;
    UNIDENTIFIED_SENDER = 6;

    // Our parser does not handle reserved in enums: DESKTOP-1569
    // reserved           7;

    PLAINTEXT_CONTENT   = 8;
  }

  optional Type   type            = 1;
  optional string sourceServiceId = 11;
  optional uint32 sourceDevice    = 7;
  optional string destinationServiceId = 13;
  // reserved                          3; // formerly optional string relay = 3;
  optional uint64 timestamp       = 5;
  // reserved                          6; // formerly optional bytes legacyMessage = 6;
  optional bytes  content         = 8; // Contains an encrypted Content
  optional string serverGuid      = 9;
  optional uint64 serverTimestamp = 10;
  optional bool ephemeral = 12; // indicates that the message should not be persisted if the recipient is offline
  optional bool urgent = 14 [default=true];  // indicates that the content is considered timely by the sender; defaults to true so senders have to opt-out to say something isn't time critical
  optional string updated_pni = 15;
  optional bool story = 16;  // indicates that the content is a story.
  optional bytes reporting_token = 17;
  // next: 18
}

message Content {
  optional DataMessage         dataMessage                  = 1;
  optional SyncMessage         syncMessage                  = 2;
  optional CallingMessage      callingMessage               = 3;
  optional NullMessage         nullMessage                  = 4;
  optional ReceiptMessage      receiptMessage               = 5;
  optional TypingMessage       typingMessage                = 6;
  optional bytes               senderKeyDistributionMessage = 7;
  optional bytes               decryptionErrorMessage       = 8;
  optional StoryMessage        storyMessage                 = 9;
  optional PniSignatureMessage pniSignatureMessage          = 10;
  optional EditMessage editMessage = 11;
}

message CallingMessage {
  message Offer {
    enum Type {
      OFFER_AUDIO_CALL = 0;
      OFFER_VIDEO_CALL = 1;
    }

    optional uint64 callId = 1;
    reserved /* sdp */       2;
    optional Type   type   = 3;
    optional bytes  opaque = 4;
  }

  message Answer {
    optional uint64 callId = 1;
    reserved /* sdp */       2;
    optional bytes  opaque = 3;
  }

  message IceCandidate {
    optional uint64 callId = 1;
    reserved /* mid */       2;
    reserved /* line */      3;
    reserved /* sdp */       4;
    optional bytes  opaque = 5;
  }

  message Busy {
    optional uint64 callId = 1;
  }

  message Hangup {
    enum Type {
      HANGUP_NORMAL          = 0;
      HANGUP_ACCEPTED        = 1;
      HANGUP_DECLINED        = 2;
      HANGUP_BUSY            = 3;
      HANGUP_NEED_PERMISSION = 4;
    }

    optional uint64 callId   = 1;
    optional Type   type     = 2;
    optional uint32 deviceId = 3;
  }

  message Opaque {
    enum Urgency {
      DROPPABLE          = 0;
      HANDLE_IMMEDIATELY = 1;
    }

    optional bytes   data    = 1;
    optional Urgency urgency = 2;
  }

  optional Offer        offer               = 1;
  optional Answer       answer              = 2;
  repeated IceCandidate iceCandidates       = 3;
  reserved /* legacyHangup */                 4;
  optional Busy         busy                = 5;
  reserved /* profileKey */                   6;
  optional Hangup       hangup              = 7;
  reserved /* multiRing */                    8;
  optional uint32       destinationDeviceId = 9;
  optional Opaque       opaque              = 10;
}

message DataMessage {
  enum Flags {
    END_SESSION             = 1;
    EXPIRATION_TIMER_UPDATE = 2;
    PROFILE_KEY_UPDATE      = 4;
  }

  message Payment {
    message Amount {
      message MobileCoin {
        optional uint64 picoMob = 1;  // 1,000,000,000,000 picoMob per Mob
      }

      oneof Amount {
        MobileCoin mobileCoin = 1;
      }
    }

    message RequestId {
      optional string uuid = 1;
    }

    message Request {
      optional RequestId requestId = 1;
      optional Amount    amount    = 2;
      optional string    note      = 3;
    }

    message Notification {
      message MobileCoin {
        optional bytes  receipt = 1;
      }

      oneof Transaction {
        MobileCoin mobileCoin = 1;
      }

      // Optional, Refers to the PaymentRequest message, if any.
      optional string    note          = 2;
      optional RequestId requestId     = 1003;
    }

    message Cancellation {
      optional RequestId requestId = 1;
    }

    message Activation {
      enum Type {
        REQUEST = 0;
        ACTIVATED = 1;
      }

      optional Type type = 1;
    }

    oneof Item {
      Notification notification = 1;
      Activation activation = 2;
      Request request  = 1002;
      Cancellation cancellation = 1003;
    }
  }

  message Quote {
    enum Type {
      NORMAL = 0;
      GIFT_BADGE = 1;
    }

    message QuotedAttachment {
      optional string            contentType = 1;
      optional string            fileName    = 2;
      optional AttachmentPointer thumbnail   = 3;
    }

    optional uint64           id          = 1;
    reserved                  /* author */  2; // removed
    optional string           authorAci  = 5;
    optional string           text        = 3;
    repeated QuotedAttachment attachments = 4;
    repeated BodyRange        bodyRanges  = 6;
    optional Type             type        = 7;
  }

  message Contact {
    message Name {
      optional string givenName   = 1;
      optional string familyName  = 2;
      optional string prefix      = 3;
      optional string suffix      = 4;
      optional string middleName  = 5;
      optional string displayName = 6;
    }

    message Phone {
      enum Type {
        HOME   = 1;
        MOBILE = 2;
        WORK   = 3;
        CUSTOM = 4;
      }

      optional string value = 1;
      optional Type   type  = 2;
      optional string label = 3;
    }

    message Email {
      enum Type {
        HOME   = 1;
        MOBILE = 2;
        WORK   = 3;
        CUSTOM = 4;
      }

      optional string value = 1;
      optional Type   type  = 2;
      optional string label = 3;
    }

    message PostalAddress {
      enum Type {
        HOME   = 1;
        WORK   = 2;
        CUSTOM = 3;
      }

      optional Type   type         = 1;
      optional string label        = 2;
      optional string street       = 3;
      optional string pobox        = 4;
      optional string neighborhood = 5;
      optional string city         = 6;
      optional string region       = 7;
      optional string postcode     = 8;
      optional string country      = 9;
    }

    message Avatar {
      optional AttachmentPointer avatar    = 1;
      optional bool              isProfile = 2;
    }

    optional Name              name            = 1;
    repeated Phone             number          = 3;
    repeated Email             email           = 4;
    repeated PostalAddress     address         = 5;
    optional Avatar            avatar          = 6;
    optional string            organization    = 7;
  }

  message Preview {
    optional string            url   = 1;
    optional string            title = 2;
    optional AttachmentPointer image = 3;
    optional string            description = 4;
    optional uint64            date        = 5;
  }

  message Sticker {
    optional bytes             packId    = 1;
    optional bytes             packKey   = 2;
    optional uint32            stickerId = 3;
    optional AttachmentPointer data      = 4;
    optional string            emoji     = 5;
  }

  message Reaction {
    optional string emoji            = 1;
    optional bool   remove           = 2;
    reserved /* targetAuthorE164 */    3; // removed
    optional string targetAuthorAci = 4;
    optional uint64 targetTimestamp  = 5;
  }

  message Delete {
    optional uint64 targetSentTimestamp = 1;
  }

  message BodyRange {
    enum Style {
      NONE = 0;
      BOLD = 1;
      ITALIC = 2;
      SPOILER = 3;
      STRIKETHROUGH = 4;
      MONOSPACE = 5;
    }

    optional uint32 start = 1;
    optional uint32 length = 2;

    oneof associatedValue {
      string mentionAci = 3;
      Style style = 4;
    }
  }

  message GroupCallUpdate {
    optional string eraId = 1;
  }

  message StoryContext {
    optional string authorAci = 1;
    optional uint64 sentTimestamp = 2;
  }

  enum ProtocolVersion {
    option allow_alias = true;

    INITIAL = 0;
    MESSAGE_TIMERS = 1;
    VIEW_ONCE = 2;
    VIEW_ONCE_VIDEO = 3;
    REACTIONS = 4;
    CDN_SELECTOR_ATTACHMENTS = 5;
    MENTIONS = 6;
    PAYMENTS = 7;
    CURRENT = 7;
  }

  message GiftBadge {
    optional bytes receiptCredentialPresentation = 1;
  }

  optional string             body                    = 1;
  repeated AttachmentPointer  attachments             = 2;
  reserved /*groupV1*/ 3;
  optional GroupContextV2     groupV2                 = 15;
  optional uint32             flags                   = 4;
  optional uint32             expireTimer             = 5;
  optional bytes              profileKey              = 6;
  optional uint64             timestamp               = 7;
  optional Quote              quote                   = 8;
  repeated Contact            contact                 = 9;
  repeated Preview            preview                 = 10;
  optional Sticker            sticker                 = 11;
  optional uint32             requiredProtocolVersion = 12;
  optional bool               isViewOnce              = 14;
  optional Reaction           reaction                = 16;
  optional Delete             delete                  = 17;
  repeated BodyRange          bodyRanges              = 18;
  optional GroupCallUpdate    groupCallUpdate         = 19;
  optional Payment            payment                 = 20;
  optional StoryContext       storyContext            = 21;
  optional GiftBadge          giftBadge               = 22;
}

message NullMessage {
  optional bytes padding = 1;
}

message ReceiptMessage {
  enum Type {
    DELIVERY = 0;
    READ     = 1;
    VIEWED   = 2;
  }

  optional Type   type      = 1;
  repeated uint64 timestamp = 2;
}

message TypingMessage {
    enum Action {
        STARTED = 0;
        STOPPED = 1;
    }

    optional uint64 timestamp = 1;
    optional Action action    = 2;
    optional bytes  groupId   = 3;
}

message StoryMessage {
  optional bytes profileKey = 1;
  optional GroupContextV2 group = 2;
  oneof attachment {
    AttachmentPointer fileAttachment = 3;
    TextAttachment textAttachment = 4;
  }
  optional bool allowsReplies = 5;
  repeated BodyRange bodyRanges = 6;
}

message TextAttachment {
  enum Style {
    DEFAULT = 0;
    REGULAR = 1;
    BOLD = 2;
    SERIF = 3;
    SCRIPT = 4;
    CONDENSED = 5;
  }

  message Gradient {
    optional uint32 startColor = 1; // deprecated: this field will be removed in a future release.
    optional uint32 endColor = 2; // deprecated: this field will be removed in a future release.
    optional uint32 angle = 3; // degrees
    repeated uint32 colors = 4;
    repeated float positions = 5; // percent from 0 to 1
  }

  optional string text = 1;
  optional Style textStyle = 2;
  optional uint32 textForegroundColor = 3; // integer representation of hex color
  optional uint32 textBackgroundColor = 4;
  optional Preview preview = 5;
  oneof background {
    Gradient gradient = 6;
    uint32 color = 7;
  }
}

message Verified {
  enum State {
    DEFAULT    = 0;
    VERIFIED   = 1;
    UNVERIFIED = 2;
  }

  optional string destination     = 1;
  optional string destinationAci  = 5;
  optional bytes  identityKey     = 2;
  optional State  state           = 3;
  optional bytes  nullMessage     = 4;
}

message SyncMessage {
  message Sent {
    message UnidentifiedDeliveryStatus {
      optional string destination = 1;
      optional string destinationServiceId = 3;
      optional bool unidentified = 2;
      reserved /* destinationPni */ 4;
      optional bytes destinationPniIdentityKey = 5; // Only set for PNI destinations
    }

    message StoryMessageRecipient {
      optional string destinationServiceId = 1;
      repeated string distributionListIds = 2;
      optional bool isAllowedToReply = 3;
    }

    optional string                     destination              = 1;
    optional string                     destinationServiceId     = 7;
    optional uint64                     timestamp                = 2;
    optional DataMessage                message                  = 3;
    optional uint64                     expirationStartTimestamp = 4;
    repeated UnidentifiedDeliveryStatus unidentifiedStatus       = 5;
    optional bool                       isRecipientUpdate        = 6 [default = false];
    optional StoryMessage storyMessage = 8;
    repeated StoryMessageRecipient storyMessageRecipients = 9;
    optional EditMessage editMessage = 10;
  }

  message Contacts {
    optional AttachmentPointer blob     = 1;
    optional bool              complete = 2 [default = false];
  }

  message Blocked {
    repeated string numbers  = 1;
    repeated string acis    = 3;
    repeated bytes  groupIds = 2;
  }

  message Request {
    enum Type {
      UNKNOWN       = 0;
      CONTACTS      = 1;
      reserved /* GROUPS */ 2;
      BLOCKED       = 3;
      CONFIGURATION = 4;
      KEYS          = 5;
      PNI_IDENTITY  = 6;
    }

    optional Type type = 1;
  }

  message Keys {
    optional bytes storageService = 1; // deprecated: this field will be removed in a future release.
    optional bytes master = 2;
  }

  message Read {
    optional string sender     = 1;
    optional string senderAci  = 3;
    optional uint64 timestamp  = 2;
  }

  message Viewed {
    optional string senderE164 = 1;
    optional string senderAci = 3;
    optional uint64 timestamp = 2;
  }

  message Configuration {
    optional bool   readReceipts                   = 1;
    optional bool   unidentifiedDeliveryIndicators = 2;
    optional bool   typingIndicators               = 3;
    reserved                                         4;
    optional uint32 provisioningVersion            = 5;
    optional bool   linkPreviews                   = 6;
  }

  message StickerPackOperation {
    enum Type {
      INSTALL = 0;
      REMOVE  = 1;
    }

    optional bytes packId  = 1;
    optional bytes packKey = 2;
    optional Type  type    = 3;
  }

  message ViewOnceOpen {
    optional string sender     = 1;
    optional string senderAci  = 3;
    optional uint64 timestamp  = 2;
  }

  message MessageRequestResponse {
    enum Type {
      UNKNOWN          = 0;
      ACCEPT           = 1;
      DELETE           = 2;
      BLOCK            = 3;
      BLOCK_AND_DELETE = 4;
      SPAM             = 5;
      BLOCK_AND_SPAM   = 6;
    }

    optional string threadE164 = 1;
    optional string threadAci = 2;
    optional bytes  groupId    = 3;
    optional Type   type       = 4;
  }

  message FetchLatest {
    enum Type {
      UNKNOWN             = 0;
      LOCAL_PROFILE       = 1;
      STORAGE_MANIFEST    = 2;
      SUBSCRIPTION_STATUS = 3;
    }

    optional Type type = 1;
  }

  message PniChangeNumber {
    optional bytes identityKeyPair = 1; // Serialized libsignal-client IdentityKeyPair
    optional bytes signedPreKey = 2; // Serialized libsignal-client SignedPreKeyRecord
    optional bytes lastResortKyberPreKey = 5; // Serialized libsignal-client KyberPreKeyRecord
    optional uint32 registrationId = 3;
    optional string newE164 = 4; // The e164 we have changed our number to
    // Next ID: 6
  }

  message CallEvent {
    enum Type {
      UNKNOWN     = 0;
      AUDIO_CALL  = 1;
      VIDEO_CALL  = 2;
      GROUP_CALL  = 3;
      AD_HOC_CALL = 4;
    }

    enum Direction {
      UNKNOWN  = 0;
      INCOMING = 1;
      OUTGOING = 2;
    }

    enum Event {
      UNKNOWN      = 0;
      ACCEPTED     = 1;
      NOT_ACCEPTED = 2;
      DELETE       = 3;
    }

    optional bytes     peerId    = 1;
    optional uint64    callId    = 2;
    optional uint64    timestamp = 3;
    optional Type      type      = 4;
    optional Direction direction = 5;
    optional Event     event     = 6;
  }

  message CallLinkUpdate {
    enum Type {
      UPDATE = 0;
      DELETE = 1;
    }

    optional bytes rootKey      = 1;
    optional bytes adminPasskey = 2;
    optional Type  type         = 3; // defaults to UPDATE
  }

  message CallLogEvent {
    enum Type {
      CLEAR = 0;
      MARKED_AS_READ = 1;
    }

    optional Type   type      = 1;
    optional uint64 timestamp = 2;
  }

  message DeleteForMe {
    message ConversationIdentifier {
      oneof identifier {
        string threadServiceId = 1;
        bytes threadGroupId = 2;
        string threadE164 = 3;
      }
    }
  
    message AddressableMessage {
      oneof author {
        string authorServiceId = 1;
        string authorE164 = 2;
      }
      optional uint64 sentTimestamp = 3;
    }

    message MessageDeletes {
      optional ConversationIdentifier conversation = 1;
      repeated AddressableMessage messages = 2;
    }

    message AttachmentDelete {
      optional ConversationIdentifier conversation = 1;
      optional AddressableMessage targetMessage = 2;
      // The `clientUuid` from `AttachmentPointer`.
      optional bytes clientUuid = 3;
      // SHA256 hash of the (encrypted, padded, etc.) attachment blob on the CDN.
      optional bytes fallbackDigest = 4;
      // SHA256 hash of the plaintext content of the attachment.
      optional bytes fallbackPlaintextHash = 5;
    }

    message ConversationDelete {
      optional ConversationIdentifier conversation = 1;
      repeated AddressableMessage mostRecentMessages = 2;
      optional bool isFullDelete = 3;
      repeated AddressableMessage mostRecentNonExpiringMessages = 4;
    }

    message LocalOnlyConversationDelete {
      optional ConversationIdentifier conversation = 1;
    }

    repeated MessageDeletes messageDeletes = 1;
    repeated ConversationDelete conversationDeletes = 2;
    repeated LocalOnlyConversationDelete localOnlyConversationDeletes = 3;
    repeated AttachmentDelete attachmentDeletes = 4;
  }
  
  optional Sent                   sent                   = 1;
  optional Contacts               contacts               = 2;
  reserved /* groups */ 3;
  optional Request                request                = 4;
  repeated Read                   read                   = 5;
  optional Blocked                blocked                = 6;
  optional Verified               verified               = 7;
  optional Configuration          configuration          = 9;
  optional bytes                  padding                = 8;
  repeated StickerPackOperation   stickerPackOperation   = 10;
  optional ViewOnceOpen           viewOnceOpen           = 11;
  optional FetchLatest            fetchLatest            = 12;
  optional Keys                   keys                   = 13;
  optional MessageRequestResponse messageRequestResponse = 14;
  reserved                                                 15; // not yet added
  repeated Viewed                 viewed                 = 16;
  reserved                                                 17; // pniIdentity
  optional PniChangeNumber        pniChangeNumber        = 18;
  optional CallEvent              callEvent              = 19;
  optional CallLinkUpdate         callLinkUpdate         = 20;
  optional CallLogEvent           callLogEvent           = 21;
  optional DeleteForMe            deleteForMe            = 22;
}

message AttachmentPointer {
  enum Flags {
    VOICE_MESSAGE = 1;
    BORDERLESS = 2;
    // Our parser does not handle reserved in enums: DESKTOP-1569
    // reserved 4;
    GIF = 8;
  }

  oneof attachment_identifier {
    fixed64 cdnId = 1;
    string cdnKey = 15;
  }
  // Cross-client identifier for this attachment among all attachments on the
  // owning message.
  optional bytes clientUuid = 20;
  optional string contentType = 2;
  optional bytes key = 3;
  optional uint32 size = 4;
  optional bytes thumbnail = 5;
  optional bytes digest = 6;
  reserved /* incrementalMac with implicit chunk sizing */ 16;
  reserved /* incrementalMac for all attachment types */ 18;
  optional bytes incrementalMac = 19;
  optional uint32 chunkSize = 17;
  optional string fileName = 7;
  optional uint32 flags = 8;
  optional uint32 width = 9;
  optional uint32 height = 10;
  optional string caption = 11;
  optional string blurHash = 12;
  optional uint64 uploadTimestamp = 13;
  optional uint32 cdnNumber = 14;
  // Next ID: 21
}

message GroupContextV2 {
  optional bytes  masterKey   = 1;
  optional uint32 revision    = 2;
  optional bytes  groupChange = 3;
}

message ContactDetails {
  message Avatar {
    optional string contentType = 1;
    optional uint32 length      = 2;
  }

  optional string   number        = 1;
  optional string   aci           = 9;
  optional string   name          = 2;
  optional Avatar   avatar        = 3;
  // reserved           4; // formerly color
  // reserved           5; // formerly verified
  // reserved           6; // formerly profileKey
  // reserved           7; // formerly blocked
  optional uint32   expireTimer   = 8;
  optional uint32   inboxPosition = 10;
}

message PniSignatureMessage {
  optional bytes pni = 1;
  // Signature *by* the PNI identity key *of* the ACI identity key
  optional bytes signature = 2;
}

message EditMessage {
  optional uint64 targetSentTimestamp = 1;
  optional DataMessage dataMessage = 2;
}