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

syntax = "proto3";

// iOS - package name determines class prefix
package StorageServiceProtos;

message StorageItem {
  // @required
  bytes key = 1;  // Randomly generated 16-byte value
  // @required
  bytes value = 2;  // Encrypted serialized bytes
}

message StorageItems {
  repeated StorageItem items = 1; // Corresponds to the requested
                                  // keys that were found
}

enum OptionalBool {
  UNSET = 0;
  TRUE = 1;
  FALSE = 2;
}

message StorageManifest {
  // @required
  uint64 version = 1; // Monotonically increasing value
  // @required
  bytes value = 2; // Encrypted ManifestRecord bytes
}

message ReadOperation {
  repeated bytes readKey = 1;
}

message WriteOperation {
  StorageManifest manifest = 1;
  repeated StorageItem insertItem = 2;
  repeated bytes deleteKey = 3;
  bool deleteAll = 4;
}

message ManifestRecord {
  message Key {
    enum Type {
      UNKNOWN = 0;
      CONTACT = 1;
      GROUPV1 = 2;
      GROUPV2 = 3;
      ACCOUNT = 4;
      STORY_DISTRIBUTION_LIST = 5;
      CALL_LINK = 7;
    }

    // @required
    bytes data = 1;
    // @required
    Type type = 2;
  }

  // @required
  uint64 version = 1;
  uint32 sourceDevice = 3;
  repeated Key keys = 2;
  bytes recordIkm = 4;
}

message StorageRecord {
  oneof record {
    ContactRecord contact = 1;
    GroupV1Record groupV1 = 2;
    GroupV2Record groupV2 = 3;
    AccountRecord account = 4;
    StoryDistributionListRecord storyDistributionList = 5;
    CallLinkRecord callLink = 7;
  }
}

// If unset - computed as the value of the first byte of SHA-256(msg=CONTACT_ID)
// modulo the count of colors. Once set the avatar color for a recipient is
// never recomputed or changed.
//
// `CONTACT_ID` is the first available identifier from the list:
// - ServiceIdToBinary(ACI)
// - E164
// - ServiceIdToBinary(PNI)
// - Group Id
enum AvatarColor {
  A100 = 0;
  A110 = 1;
  A120 = 2;
  A130 = 3;
  A140 = 4;
  A150 = 5;
  A160 = 6;
  A170 = 7;
  A180 = 8;
  A190 = 9;
  A200 = 10;
  A210 = 11;
}

message ContactRecord {
  enum IdentityState {
    DEFAULT = 0;
    VERIFIED = 1;
    UNVERIFIED = 2;
  }

  message Name {
    string given = 1;
    string family = 2;
  }

  string aci = 1;
  string e164 = 2;
  string pni = 15;
  bytes profileKey = 3;
  bytes identityKey = 4;
  IdentityState identityState = 5;
  string givenName = 6;
  string familyName = 7;
  string username = 8;
  bool blocked = 9;
  bool whitelisted = 10;
  bool archived = 11;
  bool markedUnread = 12;
  uint64 mutedUntilTimestamp = 13;
  bool hideStory = 14;
  uint64 unregisteredAtTimestamp = 16;
  string systemGivenName = 17;
  string systemFamilyName = 18;
  string systemNickname = 19;
  bool hidden = 20;
  Name nickname = 22;
  string note = 23;
  optional AvatarColor avatarColor = 24;
  bytes aciBinary = 25; // 16-byte UUID
  bytes pniBinary = 26; // 16-byte UUID
}

message GroupV1Record {
  // @required
  bytes id = 1;
  reserved /*blocked*/ 2;
  reserved /*whitelisted*/ 3;
  reserved /*archived*/ 4;
  reserved /*markedUnread*/ 5;
  reserved /*mutedUntilTimestamp*/ 6;
}

message GroupV2Record {
  enum StorySendMode {
    DEFAULT = 0;
    DISABLED = 1;
    ENABLED = 2;
  }

  // @required
  bytes masterKey = 1;
  bool blocked = 2;
  bool whitelisted = 3;
  bool archived = 4;
  bool markedUnread = 5;
  uint64 mutedUntilTimestamp = 6;
  bool dontNotifyForMentionsIfMuted = 7;
  bool hideStory = 8;
  StorySendMode storySendMode = 10;
  optional AvatarColor avatarColor = 11;
  bytes verifiedNameHash = 12; // SHA-256 of last verified group name
}

message AccountRecord {
  enum PhoneNumberSharingMode {
    UNKNOWN = 0;
    EVERYBODY = 1;
    NOBODY = 2;
  }

  message PinnedConversation {
    message Contact {
      string serviceId = 1;
      string e164 = 2;
      bytes serviceIdBinary = 3; // service ID binary (i.e. 16 byte UUID for ACI, 1 byte prefix + 16 byte UUID for PNI)
    }

    oneof identifier {
      Contact contact = 1;
      bytes legacyGroupId = 3;
      bytes groupMasterKey = 4;
    }
  }

  message Payments {
    bool enabled = 1;
    bytes paymentsEntropy = 2;
  }

  message UsernameLink {
    enum Color {
      UNKNOWN = 0;
      BLUE = 1;
      WHITE = 2;
      GREY = 3;
      OLIVE = 4;
      GREEN = 5;
      ORANGE = 6;
      PINK = 7;
      PURPLE = 8;
    }

    bytes entropy = 1; // 32 bytes of entropy used for encryption
    bytes serverId = 2; // 16 bytes of encoded UUID provided by the server
    Color color = 3; // color of the QR code itself
  }

  message IAPSubscriberData {
    bytes subscriberId = 1;

    oneof iapSubscriptionId {
      // Identifies an Android Play Store IAP subscription.
      string purchaseToken = 2;
      // Identifies an iOS App Store IAP subscription.
      uint64 originalTransactionId = 3;
    }
  }

  bytes profileKey = 1;
  string givenName = 2;
  string familyName = 3;
  string avatarUrl = 4;
  bool noteToSelfArchived = 5;
  bool readReceipts = 6;
  bool sealedSenderIndicators = 7;
  bool typingIndicators = 8;
  bool proxiedLinkPreviews = 9;    // Legacy link previews flag
  bool noteToSelfMarkedUnread = 10;
  bool linkPreviews = 11;
  PhoneNumberSharingMode phoneNumberSharingMode = 12;
  bool notDiscoverableByPhoneNumber = 13;
  repeated PinnedConversation pinnedConversations = 14;
  bool preferContactAvatars = 15;
  Payments payments = 16;
  uint32 universalExpireTimer = 17;
  reserved 18; // primarySendsSms
  string e164 = 19;
  repeated string preferredReactionEmoji = 20;
  bytes donorSubscriberID = 21;
  string donorSubscriberCurrencyCode = 22;
  bool displayBadgesOnProfile = 23;
  bool donorSubscriptionManuallyCancelled = 24;
  bool keepMutedChatsArchived = 25;
  bool myStoryPrivacyHasBeenSet = 26; // Removed 'has' prefix on spec definition to avoid name conflict.
  bool viewedOnboardingStory = 27; // Removed 'has' prefix on spec definition to avoid name conflict.
  reserved /* deprecatedStoriesDisabled */ 28;
  bool storiesDisabled = 29;
  OptionalBool storyViewReceiptsEnabled = 30;
  bool readOnboardingStory = 31;
  reserved /* seenGroupStoryEducation */ 32; // Reserved for a future feature.
  string username = 33;
  bool completedUsernameOnboarding = 34; // Removed 'has' prefix on spec definition to avoid name conflict.
  UsernameLink usernameLink = 35;
  reserved /*backupsSubscriberId*/ 36;
  reserved /*backupsSubscriberCurrencyCode*/ 37;
  reserved /*backupsSubscriptionManuallyCancelled*/ 38;
  reserved /*userHasBackup*/ 39;
  optional uint64 backupTier = 40;
  IAPSubscriberData backupSubscriberData = 41;
  optional AvatarColor avatarColor = 42;
  reserved /*backupTierHistory*/ 43;
  // 44 and 45 related to notification profiles
  bool automaticKeyVerificationDisabled = 46;
  bool seenAdminDeleteEducationDialog = 47;
}

message StoryDistributionListRecord {
  bytes identifier = 1;
  string name = 2;
  repeated string recipientServiceIds = 3;
  uint64 deletedAtTimestamp = 4;
  bool allowsReplies = 5;
  bool isBlockList = 6;
  repeated bytes recipientServiceIdsBinary = 7; // service ID binary (i.e. 16 byte UUID for ACI, 1 byte prefix + 16 byte UUID for PNI)
}

message CallLinkRecord {
  bytes rootKey = 1; // 16 bytes
  bytes adminPasskey = 2; // Non-empty when the current user is an admin
  uint64 deletedAtTimestampMs = 3; // When present and non-zero, `adminPasskey`
                                   // should be cleared
  reserved /*epoch*/ 4;
}
