// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
syntax = "proto3";

package signalservice;

option java_package = "org.signal.storageservice.storage.protos.groups";
option java_outer_classname = "GroupProtos";
option java_multiple_files = true;

message AvatarUploadAttributes {
  string key = 1;
  string credential = 2;
  string acl = 3;
  string algorithm = 4;
  string date = 5;
  string policy = 6;
  string signature = 7;
}

// Stored data

message Member {
  enum Role {
    UNKNOWN = 0;
    DEFAULT = 1;
    ADMINISTRATOR = 2;
  }

  bytes user_id = 1;
  Role role = 2;
  bytes profileKey = 3;
  bytes presentation = 4;
  uint32 joinedAtVersion = 5;
  // These two fields each decrypt to a UTF8 string
  bytes label_emoji = 6;
  bytes label_string = 7;
}

message MemberPendingProfileKey {
  Member member = 1;
  bytes addedByUserId = 2;
  uint64 timestamp = 3;  // ms since epoch
}

message MemberPendingAdminApproval {
  bytes user_id = 1;
  bytes profileKey = 2;
  bytes presentation = 3;
  uint64 timestamp = 4;  // ms since epoch
}

message MemberBanned {
  bytes user_id = 1;
  uint64 timestamp = 2;  // ms since epoch
}

message AccessControl {
  enum AccessRequired {
    UNKNOWN = 0;
    ANY = 1;
    MEMBER = 2;
    ADMINISTRATOR = 3;
    UNSATISFIABLE = 4;
  }

  AccessRequired attributes = 1;
  AccessRequired members = 2;
  AccessRequired addFromInviteLink = 3;
  AccessRequired member_label = 4;
}

message Group {
  bytes publicKey = 1;
  bytes title = 2;
  bytes description = 11;
  // The URL for this group's avatar. The content at this URL can be
  // decrypted/deserialized into a `GroupAttributeBlob`.
  string avatarUrl = 3;
  bytes disappearingMessagesTimer = 4;
  AccessControl accessControl = 5;
  uint32 version = 6;
  repeated Member members = 7;
  repeated MemberPendingProfileKey membersPendingProfileKey = 8;
  repeated MemberPendingAdminApproval membersPendingAdminApproval = 9;
  bytes inviteLinkPassword = 10;
  bool announcements_only = 12;
  repeated MemberBanned members_banned = 13;
  bool terminated = 14;
  // next: 15
}

message GroupAttributeBlob {
  oneof content {
    string title = 1;
    bytes avatar = 2;
    uint32 disappearingMessagesDuration = 3;
    string descriptionText = 4;
  }
}

message GroupInviteLink {
  message GroupInviteLinkContentsV1 {
    bytes groupMasterKey = 1;
    bytes inviteLinkPassword = 2;
  }

  oneof contents {
    GroupInviteLinkContentsV1 contentsV1 = 1;
  }
}

message GroupJoinInfo {
  bytes publicKey = 1;
  bytes title = 2;
  bytes description = 8;
  string avatar = 3;
  uint32 memberCount = 4;
  AccessControl.AccessRequired addFromInviteLink = 5;
  uint32 version = 6;
  bool pendingAdminApproval = 7;
  bool pendingAdminApprovalFull = 9;
  // next: 10
}

// Deltas

message GroupChange {

  message Actions {

    message AddMemberAction {
      Member added = 1;
      bool joinFromInviteLink = 2;
    }

    message DeleteMemberAction {
      bytes deletedUserId = 1;
    }

    message ModifyMemberRoleAction {
      bytes user_id = 1;
      Member.Role role = 2;
    }

    message ModifyMemberLabelAction {
      bytes user_id = 1;
      // These two fields each decrypt to a UTF8 string
      bytes label_emoji = 2;
      bytes label_string = 3;
    }

    message ModifyMemberProfileKeyAction {
      bytes presentation = 1;
      bytes user_id = 2;
      bytes profile_key = 3;
    }

    message AddMemberPendingProfileKeyAction {
      MemberPendingProfileKey added = 1;
    }

    message DeleteMemberPendingProfileKeyAction {
      bytes deletedUserId = 1;
    }

    message PromoteMemberPendingProfileKeyAction {
      bytes presentation = 1;
      bytes user_id = 2;
      bytes profile_key = 3;
    }

    message PromoteMemberPendingPniAciProfileKeyAction {
      bytes presentation = 1;
      bytes user_id = 2;
      bytes pni = 3;
      bytes profile_key = 4;
    }

    message AddMemberPendingAdminApprovalAction {
      MemberPendingAdminApproval added = 1;
    }

    message DeleteMemberPendingAdminApprovalAction {
      bytes deletedUserId = 1;
    }

    message PromoteMemberPendingAdminApprovalAction {
      bytes user_id = 1;
      Member.Role role = 2;
    }

    message AddMemberBannedAction {
      MemberBanned added = 1;
    }

    message DeleteMemberBannedAction {
      bytes deletedUserId = 1;
    }

    message ModifyTitleAction {
      bytes title = 1;
    }

    message ModifyDescriptionAction {
      bytes description = 1;
    }

    message ModifyAvatarAction {
      string avatar = 1;
    }

    message ModifyDisappearingMessageTimerAction {
      bytes timer = 1;
    }

    message ModifyAttributesAccessControlAction {
      AccessControl.AccessRequired attributesAccess = 1;
    }

    message ModifyMembersAccessControlAction {
      AccessControl.AccessRequired membersAccess = 1;
    }

    message ModifyAddFromInviteLinkAccessControlAction {
      AccessControl.AccessRequired addFromInviteLinkAccess = 1;
    }

    message ModifyMemberLabelAccessControlAction {
      AccessControl.AccessRequired member_label_access = 1;
    }

    message ModifyInviteLinkPasswordAction {
      bytes inviteLinkPassword = 1;
    }

    message ModifyAnnouncementsOnlyAction {
      bool announcements_only = 1;
    }

    message TerminateGroupAction {}

    bytes sourceUserId = 1;
    // clients should not provide this value; the server will provide it in the response buffer to ensure the signature is binding to a particular group
    // if clients set it during a request the server will respond with 400.
    bytes group_id = 25;
    uint32 version = 2;

    repeated AddMemberAction addMembers = 3;
    repeated DeleteMemberAction deleteMembers = 4;
    repeated ModifyMemberRoleAction modifyMemberRoles = 5;
    repeated ModifyMemberLabelAction modifyMemberLabels = 26; // change epoch = 6
    repeated ModifyMemberProfileKeyAction modifyMemberProfileKeys = 6;
    repeated AddMemberPendingProfileKeyAction addMembersPendingProfileKey = 7;
    repeated DeleteMemberPendingProfileKeyAction deleteMembersPendingProfileKey = 8;
    repeated PromoteMemberPendingProfileKeyAction promoteMembersPendingProfileKey = 9;
    ModifyTitleAction modifyTitle = 10;
    ModifyAvatarAction modifyAvatar = 11;
    ModifyDisappearingMessageTimerAction modifyDisappearingMessageTimer = 12;
    ModifyAttributesAccessControlAction modifyAttributesAccess = 13;
    ModifyMembersAccessControlAction modifyMemberAccess = 14;
    ModifyAddFromInviteLinkAccessControlAction modifyAddFromInviteLinkAccess = 15;  // change epoch = 1
    ModifyMemberLabelAccessControlAction modify_member_label_access = 27; // change epoch = 6
    repeated AddMemberPendingAdminApprovalAction addMembersPendingAdminApproval = 16;  // change epoch = 1
    repeated DeleteMemberPendingAdminApprovalAction deleteMembersPendingAdminApproval = 17;  // change epoch = 1
    repeated PromoteMemberPendingAdminApprovalAction promoteMembersPendingAdminApproval = 18;  // change epoch = 1
    ModifyInviteLinkPasswordAction modifyInviteLinkPassword = 19;  // change epoch = 1
    ModifyDescriptionAction modifyDescription = 20;  // change epoch = 2
    ModifyAnnouncementsOnlyAction modify_announcements_only = 21;  // change epoch = 3
    repeated AddMemberBannedAction add_members_banned = 22;  // change epoch = 4
    repeated DeleteMemberBannedAction delete_members_banned = 23;  // change epoch = 4
    repeated PromoteMemberPendingPniAciProfileKeyAction promote_members_pending_pni_aci_profile_key = 24;  // change epoch = 5
    TerminateGroupAction terminate_group = 28; // change epoch = 7
    // next: 29
  }

  bytes actions = 1;
  bytes serverSignature = 2;
  uint32 changeEpoch = 3;
}

// External credentials

message ExternalGroupCredential {
  string token = 1;
}

// API responses

message GroupResponse {
  Group group = 1;
  bytes group_send_endorsements_response = 2;
}

message GroupChanges {
  message GroupChangeState {
    GroupChange groupChange = 1;
    Group groupState = 2;
  }

  repeated GroupChangeState groupChanges = 1;
  bytes group_send_endorsements_response = 2;
}

message GroupChangeResponse {
  GroupChange group_change = 1;
  bytes group_send_endorsements_response = 2;
}
