// ## Data Synchronisation Messages
//
// All strings are UTF-8 encoded.
//
// ### General Information
//
// When creating a sync message, only provide fields which are required and
// fields whose values have changed (i.e. delta updates). Fields that are
// required will be explicitly marked. For any other field within a sync
// message, assume that it is optional.
//
// When receiving a sync message, only handle fields whose values have been
// provided. As a rule of thumb, if a field is not present it means that the
// value has not changed.
//
// If more specific rules are needed in order to handle a field correctly,
// a description will be provided for the specific field.
//
// ### Validation
//
// A sync message should be validated by the following general rules:
//
// - Strictly validate enums, i.e. unknown values are not allowed and no
//   silent fallbacks are allowed.
// - Empty strings/lists have a different semantic than an omitted string/list.
//   The field should have a concise description on whether emptiness is
//   allowed, and, if so, the meaning of _empty_.
// - Follow the description of each field for more specific validation.
//
// If any of these rules is violated, the message should be discarded and logged
// as an error (but still acknowledged, if necessary).

syntax = "proto3";

package d2d_sync;

option java_package = "ch.threema.protobuf.d2d.sync";
option java_multiple_files = true;
option php_namespace = "Threema\\Protocols\\D2d\\Sync";

import "common.proto";

// _Read_ receipt policy (when an unread message has been read)
enum ReadReceiptPolicy {
  // Send _read_ receipt when an unread message has been read
  SEND_READ_RECEIPT = 0;
  // Don't send _read_ receipts
  DONT_SEND_READ_RECEIPT = 1;
}

// Typing indicator policy (signal _currently typing_)
enum TypingIndicatorPolicy {
  // Send _typing_ indicator when a message is being composed
  SEND_TYPING_INDICATOR = 0;
  // Don't send _typing_ indicators
  DONT_SEND_TYPING_INDICATOR = 1;
}

// Availability status category.
enum WorkAvailabilityStatusCategory {
  // None (i.e. no (un)availability).
  NONE = 0;
  // Unavailable (i.e. for generic signalling of unavailability).
  UNAVAILABLE = 1;
  // Busy.
  BUSY = 2;
}

// Availability status of the
//
// - user, or,
// - a contact, sourced from the _Work Sync_ or _Work Contacts_ or _Work
//   Directory_ endpoint.
//
// Note 1: The default is `WorkAvailabilityStatusCategory.NONE` and no
// description (i.e. empty string). Supplying these values explicitly is also
// the way to _reset_ the availability status.
//
// Note 2: Supplying a description with `WorkAvailabilityStatusCategory.NONE` is
// bogus and will be treated as `WorkAvailabilityStatusCategory.NONE` without a
// description.
message WorkAvailabilityStatus {
  // Status category.
  WorkAvailabilityStatusCategory category = 1;

  // Optional custom status description as chosen by the contact. Must be ≤
  // 256 bytes. An empty string means no custom status description is
  // present.
  string description = 2;
}

// Visibility of a conversation.
enum ConversationVisibility {
  // Appears normally in the list of conversations
  NORMAL = 0;
  // Appears pinned in the list of conversations
  PINNED = 2;
  // Appears in the archived list of conversations
  ARCHIVED = 1;
}

// Category of a conversation.
enum ConversationCategory {
  // No specific (default) category
  DEFAULT = 0;
  // Protected conversation (_private chat_)
  PROTECTED = 1;
}

// Application configuration parameters shared across Threema Work devices.
//
// See [mdm-parameters.md](./md-parameters.md) for documentation of possible
// parameter values and associated steps to apply when a parameter has been set
// for the first time, modified, or removed.
//
// Note: MDM parameters are always transmitted fully, not as delta updates.
message MdmParameters {
  // A single MDM parameter.
  message Parameter {
    oneof value {
      // String parameter
      string string_value = 1;
      // Integer parameter
      uint64 integer_value = 3;
      // Boolean parameter
      bool boolean_value = 2;
    }
  }

  // A map of MDM parameters, originating from an external MDM system. The map
  // key is the identifier of the MDM parameter (e.g. `th_nickname`).
  map<string, Parameter> external_parameters = 1;

  // A map of MDM parameters, originating from Threema App Configuration. The
  // map key is the identifier of the MDM parameter (e.g. `th_nickname`).
  map<string, Parameter> threema_parameters = 2;

  enum ParameterPrecedence {
    // On conflict, parameters defined by Threema App Configuration take
    // precedence
    THREEMA = 0;
    // On conflict, parameters defined by an external MDM take precedence
    EXTERNAL = 1;
  }
  // The parameter precedence defines how to resolve conflicting parameters
  // between an external MDM and the Threema App Configuration.
  ParameterPrecedence parameter_precedence = 3;
}

// Threema Work credentials for authentication towards Work APIs.
message ThreemaWorkCredentials {
  // Work username.
  string username = 1;
  // Work password.
  string password = 2;
}

// The user's profile.
message UserProfile {
  // Nickname
  //
  // Required towards a new device. Optional otherwise.
  //
  // When the user cleared its nickname, send an empty string. Do not send the
  // user's Threema ID (i.e. process data).
  //
  // When an empty string was received, store the empty string as-is and fall
  // back to the users Threema ID when sending an end-to-end encrypted message.
  optional string nickname = 1;

  // Profile picture
  //
  // Always optional.
  common.DeltaImage profile_picture = 2;

  // Profile picture share policy
  //
  // Required towards a new device. Optional otherwise.
  message ProfilePictureShareWith {
    oneof policy {
      // Don't share
      common.Unit nobody = 1;
      // Share with everyone
      common.Unit everyone = 2;
      // Share only with explicitly listed contacts
      //
      // When the user selected _allow list_ but did not select any contacts,
      // send an empty list. Do not fall back to `nobody`.
      common.Identities allow_list = 3;
    }
  }
  ProfilePictureShareWith profile_picture_share_with = 3;

  // External entities linked with the identity
  //
  // Required towards a new device. Optional otherwise.
  //
  // When the user has cleared all of its identity links, this message is
  // present but `links` contains zero items.
  message IdentityLinks {
    // Threema ID link.
    message IdentityLink {
      // Identity link type
      oneof type {
        // Linked with a verified telephone number (E.164 format without leading
        // `+`)
        string phone_number = 1;
        // Linked with a verified email address
        string email = 2;
      }

      // Identity link description
      string description = 3;
    }

    // List of identity links
    repeated IdentityLink links = 1;
  }
  IdentityLinks identity_links = 4;

  // Availability status of the user.
  //
  // Shall not be present in the _Consumer_ flavour.
  //
  // Always optional in the _Work_ flavour.
  WorkAvailabilityStatus work_availability_status = 5;
}

// Threema contact.
message Contact {
  // Contact items
  // -------------

  // Threema ID of the contact
  //
  // Always required.
  string identity = 1;

  // Public key of the contact
  //
  // Required towards a new device and for a new contact. Should not be set
  // for existing contacts and must be ignored (a public key for an identity
  // cannot be changed).
  optional bytes public_key = 2;

  // Unix-ish timestamp in milliseconds when the contact has been created
  // (added) locally.
  //
  // Required towards a new device and for a new contact. Optional for an
  // existing contact.
  optional uint64 created_at = 3;

  // First name of the contact
  //
  // Always optional.
  //
  // An empty string is valid. When both `first_name` and `last_name` are
  // empty string, the `nickname` should be used as display name.
  optional string first_name = 4;

  // Last name of the contact
  //
  // Always optional.
  //
  // An empty string is valid. When both `first_name` and `last_name` are
  // empty string, the `nickname` should be used as display name.
  optional string last_name = 5;

  // Nickname of the contact (without `~` prefix)
  //
  // Always optional.
  //
  // An empty string is valid. If `first_name`, `last_name` and `nickname` are
  // empty string, the Threema ID should be used as display name.
  optional string nickname = 6;

  // Verification level of the contact
  //
  // Required towards a new device and for a new contact. Optional for an
  // existing contact.
  //
  // Note: When applying logic depending on the verification level, a
  // `WorkVerificationLevel` of `WORK_SUBSCRIPTION_VERIFIED` virtually raises
  // the verification level to `SERVER_VERIFIED`. However, the contact
  // verification level takes precedence if it is `FULLY_VERIFIED`.
  enum VerificationLevel {
    // Unverified, public key has been obtained from the server
    UNVERIFIED = 0;
    // Verified with one of the account links via the server, or the contact
    // has been obtained via the Work API.
    SERVER_VERIFIED = 1;
    // Verified, public key has been obtained via a secure channel
    FULLY_VERIFIED = 2;
  }
  optional VerificationLevel verification_level = 7;

  // Threema Work verification level of the contact, sourced from the _Work
  // Sync_ or _Work Contacts_ endpoint.
  //
  // Required towards a new device and for a new contact. Optional for an
  // existing contact.
  //
  // Note: When not using a Threema Work client, the Threema Work verification
  // level must always be `NONE`.
  enum WorkVerificationLevel {
    // The user does not use Threema Work or the contact is not in the same
    // Threema Work subscription.
    NONE = 0;
    // The contact is in the same Threema Work subscription.
    WORK_SUBSCRIPTION_VERIFIED = 1;
  }
  optional WorkVerificationLevel work_verification_level = 21;

  // Identity type of the contact
  //
  // Required towards a new device and for a new contact. Optional for an
  // existing contact.
  enum IdentityType {
    // Regular contact (uses the regular Threema app)
    REGULAR = 0;
    // Work contact (uses the Threema work app)
    WORK = 1;
  }
  optional IdentityType identity_type = 8;

  // Acquaintance level of the contact
  //
  // Required towards a new device and for a new contact. Optional for an
  // existing contact.
  enum AcquaintanceLevel {
    // The contact was explicitly added by the user, the Work subscription
    // administrator or a 1:1 conversation with the contact has been initiated.
    DIRECT = 0;
    // This level covers the following cases:
    //
    // - The contact is part of a group the user is also part of. The contact
    //   was not explicitly added and no 1:1 conversation has been initiated.
    // - The contact was part of a group the user was/is also part of before but
    //   either of them has since been removed from all common groups.
    // - The contact has been explicitly removed by the user.
    //
    // The protocol refers to contacts with this level as either acquaintance
    // level _group_ or _deleted_. They are interchangeable and there's no
    // semantic difference between them beyond the context in which they are
    // referred to.
    GROUP_OR_DELETED = 1;
  }
  optional AcquaintanceLevel acquaintance_level = 9;

  // Activity state of the contact
  //
  // Required towards a new device and for a new contact. Optional for an
  // existing contact.
  enum ActivityState {
    // The ID is active (used recently and not deleted).
    ACTIVE = 0;
    // The ID has not been used for a prolonged time (typically 3 months) and
    // may have been abandoned. Such IDs can be marked as "inactive" in the
    // client's contact database, and the user may be alerted to the fact that
    // messages to this ID may not arrive (e.g. alert box, gray color in
    // contact list etc.). An inactive ID can become active again at any time,
    // so the client should keep checking it.
    INACTIVE = 1;
    // ID has never been assigned, or it has been permanently deleted. Such IDs
    // should be marked as "deleted" in the client's contact database, and no
    // further messages should be sent or received from them. Since deletion is
    // permanent, once an ID has been reported as invalid by the server, it
    // should not be checked again in the future.
    INVALID = 2;
  }
  optional ActivityState activity_state = 10;

  // Features available for the contact (64 bit mask,
  // `common.CspFeatureMaskFlag` logically or-ed).
  //
  // Required towards a new device and for a new contact. Optional for an
  // existing contact.
  optional uint64 feature_mask = 18;

  // Contact synchronisation state
  //
  // Required towards a new device and for a new contact. Optional for an
  // existing contact.
  //
  // These states are strict monotonic. When a downgrade is being detected,
  // log the incident and ignore the update.
  enum SyncState {
    // The contact data has not been imported and has not been edited by the
    // user either.
    INITIAL = 0;
    // The contact data has been imported (e.g. via a local address book and an
    // identity link). In this state, subsequent contact synchronisations must
    // not alter the contact's data.
    IMPORTED = 1;
    // The contact data has been edited by the user. In this state, subsequent
    // contact synchronisations must not alter the contact's data.
    CUSTOM = 2;
  }
  optional SyncState sync_state = 13;

  // Contact-defined profile picture as received from the contact in a
  // `set-profile-picture` message.
  //
  // Always optional.
  common.DeltaImage contact_defined_profile_picture = 14;

  // User-defined profile picture set by the user or imported from the address
  // book.
  //
  // Always optional.
  common.DeltaImage user_defined_profile_picture = 15;

  // Server-reported UNIX-ish timestamp in milliseconds of the last incident all
  // work properties of this contact were retrieved (and any potential changes
  // were applied) from a _Work Sync_, a _Work Contacts_, or a _Work Directory_
  // endpoint.
  //
  // Shall not be present in the _Consumer_ flavour.
  //
  // Always optional in the _Work_ flavour.
  optional uint64 work_last_full_sync_at = 26;

  // Availability status of the contact.
  //
  // Shall not be present in the _Consumer_ flavour.
  //
  // Always optional in the _Work_ flavour.
  WorkAvailabilityStatus work_availability_status = 27;

  // Conversation items
  // ------------------

  // _Read_ receipt policy override for this contact
  //
  // Required towards a new device and for a new contact. Optional for an
  // existing contact.
  message ReadReceiptPolicyOverride {
    oneof override {
      // Apply the _read_ receipt policy specified in the settings
      common.Unit default = 1;
      // Apply the following _read_ receipt policy
      ReadReceiptPolicy policy = 2;
    }
  }
  ReadReceiptPolicyOverride read_receipt_policy_override = 16;

  // Typing indicator policy override for this contact
  //
  // Required towards a new device and for a new contact. Optional for an
  // existing contact.
  message TypingIndicatorPolicyOverride {
    oneof override {
      // Apply the typing indicator policy specified in the settings
      common.Unit default = 1;
      // Apply the following typing indicator policy
      TypingIndicatorPolicy policy = 2;
    }
  }
  TypingIndicatorPolicyOverride typing_indicator_policy_override = 17;

  // Notification trigger policy for the contact
  //
  // Required towards a new device and for a new contact. Optional for an
  // existing contact.
  message NotificationTriggerPolicyOverride {
    message Policy {
      // Apply the following notification trigger policy
      enum NotificationTriggerPolicy {
        // Never trigger a notification message.
        NEVER = 0;
      }
      NotificationTriggerPolicy policy = 1;

      // Unix-ish timestamp in milliseconds when the provided policy should
      // expire and fall back to the default. If not provided, the policy does
      // not expire.
      optional uint64 expires_at = 2;
    }

    oneof override {
      // Apply the trigger policy specified in the settings (i.e. trigger on
      // every _conversation_ message).
      common.Unit default = 1;
      // Apply the following notification trigger policy
      Policy policy = 2;
    }
  }
  NotificationTriggerPolicyOverride notification_trigger_policy_override = 19;

  // Deprecated notification sound policy.
  //
  // Required towards a new device and for a new contact until the field has
  // been phased out. Optional for an existing contact.
  message DeprecatedNotificationSoundPolicyOverride {
    oneof override {
      common.Unit default = 1;
    }
  }
  DeprecatedNotificationSoundPolicyOverride deprecated_notification_sound_policy_override = 20 [deprecated = true];

  // Conversation category of the contact
  //
  // Required towards a new device and for a new contact. Optional for an
  // existing contact.
  optional ConversationCategory conversation_category = 11;

  // Conversation visibility of the contact
  //
  // Required towards a new device and for a new contact. Optional for an
  // existing contact.
  optional ConversationVisibility conversation_visibility = 12;
}

// Threema contacts associated to a group.
message Group {
  // Group items
  // -----------

  // Unique group identity
  //
  // Always required.
  common.GroupIdentity group_identity = 1;

  // Name of the group
  //
  // Required towards a new device and for a new group. Optional for an
  // existing group.
  //
  // An empty string is valid. In such a case, the display name of the
  // group is the list of its members.
  optional string name = 2;

  // Unix-ish timestamp in milliseconds when the group has been created locally
  //
  // Required towards a new device and for a new group. Optional for an
  // existing group.
  optional uint64 created_at = 3;

  // The user's state within the group
  //
  // Required towards a new device and for a new group. Optional for an
  // existing group.
  enum UserState {
    // The user is a member (or creator) of the group.
    MEMBER = 0;
    // The user has been kicked from the group. Implies that the group has been
    // marked as _left_.
    KICKED = 1;
    // The user left the group. Implies that the group has been marked as
    // _left_. If the user is the creator, this implies that the group has been
    // disbanded.
    LEFT = 2;
  }
  optional UserState user_state = 6;

  // Group's profile picture as received from the group's creator
  //
  // Always optional.
  common.DeltaImage profile_picture = 7;

  // Group members (**NOT** including the creator and the user itself)
  //
  // Required towards a new device and for a new group. Optional for an existing
  // group.
  //
  // The concrete semantic of this list depends on the current `user_state`:
  //
  // - `MEMBER`: It contains a list of all **current** group members (with the
  //   user itself implicitly added).
  // - `KICKED`/`LEFT`: It contains a list of all **previous** group members up
  //   to that event. If the list is empty, the group must not be visible in the
  //   UI.
  //
  // An empty list is valid.
  common.Identities member_identities = 8;

  // Conversation items
  // ------------------

  // Notification trigger policy for the group
  //
  // Required towards a new device and for a new group. Optional for an
  // existing group.
  message NotificationTriggerPolicyOverride {
    message Policy {
      // Apply the following notification trigger policy
      enum NotificationTriggerPolicy {
        // Only trigger a notification if mentioned in a _conversation_ message.
        MENTIONED = 0;
        // Never trigger a notification message.
        NEVER = 1;
      }
      NotificationTriggerPolicy policy = 1;

      // Unix-ish timestamp in milliseconds when the provided policy should
      // expire and fall back to the default. If not provided, the policy does
      // not expire.
      optional uint64 expires_at = 2;
    }

    oneof override {
      // Apply the trigger policy specified in the settings (i.e. trigger on
      // every _conversation_ message).
      common.Unit default = 1;
      // Apply the following notification trigger policy
      Policy policy = 2;
    }
  }
  NotificationTriggerPolicyOverride notification_trigger_policy_override = 9;

  // Deprecated notification sound policy.
  //
  // Required towards a new device and for a new group until the field has
  // been phased out. Optional for an existing group.
  message DeprecatedNotificationSoundPolicyOverride {
    oneof override {
      common.Unit default = 1;
    }
  }
  DeprecatedNotificationSoundPolicyOverride deprecated_notification_sound_policy_override = 10 [deprecated = true];

  // Conversation category of the group
  //
  // Required towards a new device and for a new group. Optional for an
  // existing group.
  optional ConversationCategory conversation_category = 4;

  // Conversation visibility of the group
  //
  // Required towards a new device and for a new group. Optional for an
  // existing group.
  optional ConversationVisibility conversation_visibility = 5;
}

// Threema contacts associated to a distribution list.
message DistributionList {
  // Distribution list items
  // -----------------------

  // Unique ID of the distribution list
  //
  // Always required.
  fixed64 distribution_list_id = 1;

  // Name of the distribution list
  //
  // Required towards a new device and for a new distribution list. Optional
  // for an existing distribution list.
  //
  // An empty string is valid. In such a case, the display name of the
  // distribution list is the list of its members.
  optional string name = 2;

  // Unix-ish timestamp in milliseconds when the group has been created
  //
  // Required towards a new device and for a new distribution list. Optional
  // for an existing distribution list.
  optional uint64 created_at = 3;

  // Distribution list members
  //
  // Required towards a new device and for a new distribution list. Optional
  // for an existing distribution list.
  //
  // An empty list is **not** valid. Clearing all members should be prevented
  // by the UI.
  common.Identities member_identities = 6;

  // Conversation items
  // ------------------

  // Conversation category of the distribution list
  //
  // Required towards a new device and for a new distribution list. Optional
  // for an existing distribution list.
  optional ConversationCategory conversation_category = 4;

  // Conversation visibility of the distribution list
  //
  // Required towards a new device and for a new distribution list. Optional
  // for an existing distribution list.
  optional ConversationVisibility conversation_visibility = 5;
}

// App settings
//
// When the user changes one or more settings:
//
// 1. Begin a transaction (scope: `SETTINGS_SYNC`, precondition: none).
// 2. For each setting that has been modified, run the associated steps of the
//    setting and include the modified settings.
// 3. Reflect this message and commit the transaction.
// 4. Apply the modified settings locally.
//
// When reflected from another device:
//
// 1. For each setting that is being included by this message, run the
//    associated steps of the setting and apply the modified setting.
message Settings {
  // Contact synchronisation policy
  //
  // Required towards a new device. Optional otherwise.
  enum ContactSyncPolicy {
    // Not synced
    NOT_SYNCED = 0;
    // Synced
    SYNC = 1;
  }
  optional ContactSyncPolicy contact_sync_policy = 1;

  // Unknown contacts policy
  //
  // Required towards a new device. Optional otherwise.
  enum UnknownContactPolicy {
    // Allowed to contact the user
    ALLOW_UNKNOWN = 0;
    // Will be blocked by the user
    BLOCK_UNKNOWN = 1;
  }
  optional UnknownContactPolicy unknown_contact_policy = 2;

  // _Read_ receipt policy (when an unread message has been read)
  //
  // Required towards a new device. Optional otherwise.
  optional ReadReceiptPolicy read_receipt_policy = 3;

  // Typing indicator policy (signal _currently typing_)
  //
  // Required towards a new device. Optional otherwise.
  optional TypingIndicatorPolicy typing_indicator_policy = 4;

  // Threema 1:1 Call policy
  //
  // Required towards a new device. Optional otherwise.
  enum O2oCallPolicy {
    // Allow creating/receiving Threema 1:1 Calls
    ALLOW_O2O_CALL = 0;
    // Denied from creating/receiving any Threema 1:1 Calls
    DENY_O2O_CALL = 1;
  }
  optional O2oCallPolicy o2o_call_policy = 5;

  // Threema 1:1 Call connection policy.
  //
  // Required towards a new device. Optional otherwise.
  //
  // Note: This is only relevant for 1:1 calls.
  enum O2oCallConnectionPolicy {
    // Allow direct (peer-to-peer) connections for Threema 1:1 Calls
    ALLOW_DIRECT_CONNECTION = 0;
    // Require relayed connections for Threema 1:1 Calls
    REQUIRE_RELAYED_CONNECTION = 1;
  }
  optional O2oCallConnectionPolicy o2o_call_connection_policy = 6;

  // Threema 1:1 Call video (stream) policy.
  //
  // Required towards a new device. Optional otherwise.
  enum O2oCallVideoPolicy {
    // Allow sending and receiving video streams in Threema 1:1 Calls.
    ALLOW_VIDEO = 0;
    // Reject and don't send video streams in Threema 1:1 Calls.
    DENY_VIDEO = 1;
  }
  optional O2oCallVideoPolicy o2o_call_video_policy = 12;

  // Threema Group Call policy
  //
  // Required towards a new device. Optional otherwise.
  enum GroupCallPolicy {
    // Allow creating/receiving Threema Group Calls
    ALLOW_GROUP_CALL = 0;
    // Denied from creating/receiving any Threema Group Calls
    DENY_GROUP_CALL = 1;
  }
  optional GroupCallPolicy group_call_policy = 11;

  // Screenshot policy
  //
  // Required towards a new device. Optional otherwise.
  enum ScreenshotPolicy {
    // Allow taking screenshots
    ALLOW_SCREENSHOT = 0;
    // Deny taking screenshots, if possible
    DENY_SCREENSHOT = 1;
  }
  optional ScreenshotPolicy screenshot_policy = 7;

  // Keyboard data collection policy (e.g. for personalised suggestions)
  //
  // Required towards a new device. Optional otherwise.
  enum KeyboardDataCollectionPolicy {
    // Allow keyboard input data to be collected
    ALLOW_DATA_COLLECTION = 0;
    // Deny collecting of keyboard input data
    DENY_DATA_COLLECTION = 1;
  }
  optional KeyboardDataCollectionPolicy keyboard_data_collection_policy = 8;

  // List of Threema IDs whose messages are blocked
  //
  // Required towards a new device. Optional otherwise.
  //
  // An empty list is valid.
  common.Identities blocked_identities = 9;

  // Threema IDs to be excluded when syncing the contact list
  //
  // Required towards a new device. Optional otherwise.
  //
  // An empty list is valid.
  common.Identities exclude_from_sync_identities = 10;
}
