syntax = "proto3";

package bep;

// --- Pre-auth ---

message Hello {
  string device_name = 1;
  string client_name = 2;
  string client_version = 3;
  int32 num_connections = 4;
  int64 timestamp = 5;
}

// --- Header ---

message Header {
  MessageType type = 1;
  MessageCompression compression = 2;
}

enum MessageType {
  MESSAGE_TYPE_CLUSTER_CONFIG = 0;
  MESSAGE_TYPE_INDEX = 1;
  MESSAGE_TYPE_INDEX_UPDATE = 2;
  MESSAGE_TYPE_REQUEST = 3;
  MESSAGE_TYPE_RESPONSE = 4;
  MESSAGE_TYPE_DOWNLOAD_PROGRESS = 5;
  MESSAGE_TYPE_PING = 6;
  MESSAGE_TYPE_CLOSE = 7;
}

enum MessageCompression {
  MESSAGE_COMPRESSION_NONE = 0;
  MESSAGE_COMPRESSION_LZ4 = 1;
}

// --- Actual messages ---

// Cluster Config

message ClusterConfig {
  repeated Folder folders = 1;
  bool secondary = 2;
}

message Folder {
  string id = 1;
  string label = 2;
  bool read_only = 3;
  bool ignore_permissions = 4;
  bool ignore_delete = 5;
  bool disable_temp_indexes = 6;
  bool paused = 7;

  repeated Device devices = 16;
}

message Device {
  bytes id = 1;
  string name = 2;
  repeated string addresses = 3;
  Compression compression = 4;
  string cert_name = 5;
  int64 max_sequence = 6;
  bool introducer = 7;
  uint64 index_id = 8;
  bool skip_introduction_removals = 9;
  bytes encryption_password_token = 10;
}

enum Compression {
  COMPRESSION_METADATA = 0;
  COMPRESSION_NEVER = 1;
  COMPRESSION_ALWAYS = 2;
}

// Index and Index Update

message Index {
  string folder = 1;
  repeated FileInfo files = 2;
  int64 last_sequence = 3; // the highest sequence in this batch
}

message IndexUpdate {
  string folder = 1;
  repeated FileInfo files = 2;
  int64 last_sequence = 3; // the highest sequence in this batch
  int64 prev_sequence = 4; // the highest sequence in the previous batch
}

message FileInfo {
  // The field ordering here optimizes for struct size / alignment --
  // large types come before smaller ones.

  string name = 1;
  int64 size = 3;
  int64 modified_s = 5;
  uint64 modified_by = 12;
  Vector version = 9;
  int64 sequence = 10;
  repeated BlockInfo blocks = 16;
  bytes symlink_target = 17;
  bytes blocks_hash = 18;
  bytes encrypted = 19;
  FileInfoType type = 2;
  uint32 permissions = 4;
  int32 modified_ns = 11;
  int32 block_size = 13;
  PlatformData platform = 14;

  // The local_flags fields stores flags that are relevant to the local
  // host only. It is not part of the protocol, doesn't get sent or
  // received (we make sure to zero it), nonetheless we need it on our
  // struct and to be able to serialize it to/from the database.
  uint32 local_flags = 1000;

  // The version_hash is an implementation detail and not part of the wire
  // format.
  bytes version_hash = 1001;

  // The time when the inode was last changed (i.e., permissions, xattrs
  // etc changed). This is host-local, not sent over the wire.
  int64 inode_change_ns = 1002;

  // The size of the data appended to the encrypted file on disk. This is
  // host-local, not sent over the wire.
  int32 encryption_trailer_size = 1003;

  bool deleted = 6;
  bool invalid = 7;
  bool no_permissions = 8;
}

enum FileInfoType {
  FILE_INFO_TYPE_FILE = 0;
  FILE_INFO_TYPE_DIRECTORY = 1;
  FILE_INFO_TYPE_SYMLINK_FILE = 2 [deprecated = true];
  FILE_INFO_TYPE_SYMLINK_DIRECTORY = 3 [deprecated = true];
  FILE_INFO_TYPE_SYMLINK = 4;
}

message BlockInfo {
  bytes hash = 3;
  int64 offset = 1;
  int32 size = 2;
  uint32 weak_hash = 4;
}

message Vector {
  repeated Counter counters = 1;
}

message Counter {
  uint64 id = 1;
  uint64 value = 2;
}

message PlatformData {
  UnixData unix = 1;
  WindowsData windows = 2;
  XattrData linux = 3;
  XattrData darwin = 4;
  XattrData freebsd = 5;
  XattrData netbsd = 6;
}

message UnixData {
  // The owner name and group name are set when known (i.e., could be
  // resolved on the source device), while the UID and GID are always set
  // as they come directly from the stat() call.
  string owner_name = 1;
  string group_name = 2;
  int32 uid = 3;
  int32 gid = 4;
}

message WindowsData {
  // Windows file objects have a single owner, which may be a user or a
  // group. We keep the name of that account, and a flag to indicate what
  // type it is.
  string owner_name = 1;
  bool owner_is_group = 2;
}

message XattrData {
  repeated Xattr xattrs = 1;
}

message Xattr {
  string name = 1;
  bytes value = 2;
}

// Request

message Request {
  int32 id = 1;
  string folder = 2;
  string name = 3;
  int64 offset = 4;
  int32 size = 5;
  bytes hash = 6;
  bool from_temporary = 7;
  uint32 weak_hash = 8;
  int32 block_no = 9;
}

// Response

message Response {
  int32 id = 1;
  bytes data = 2;
  ErrorCode code = 3;
}

enum ErrorCode {
  ERROR_CODE_NO_ERROR = 0;
  ERROR_CODE_GENERIC = 1;
  ERROR_CODE_NO_SUCH_FILE = 2;
  ERROR_CODE_INVALID_FILE = 3;
}

// DownloadProgress

message DownloadProgress {
  string folder = 1;
  repeated FileDownloadProgressUpdate updates = 2;
}

message FileDownloadProgressUpdate {
  FileDownloadProgressUpdateType update_type = 1;
  string name = 2;
  Vector version = 3;
  repeated int32 block_indexes = 4 [packed = false];
  int32 block_size = 5;
}

enum FileDownloadProgressUpdateType {
  FILE_DOWNLOAD_PROGRESS_UPDATE_TYPE_APPEND = 0;
  FILE_DOWNLOAD_PROGRESS_UPDATE_TYPE_FORGET = 1;
}

// Ping

message Ping {}

// Close

message Close {
  string reason = 1;
}
