mirror of
https://github.com/oxen-io/session-open-group-server.git
synced 2023-12-13 20:30:35 +01:00
315 lines
12 KiB
Rust
315 lines
12 KiB
Rust
use base64;
|
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
pub struct User {
|
|
pub id: i64,
|
|
pub session_id: String,
|
|
pub created: f64,
|
|
pub last_active: f64,
|
|
pub banned: bool,
|
|
pub moderator: bool,
|
|
pub admin: bool,
|
|
}
|
|
|
|
impl User {
|
|
pub fn from_row(row: &rusqlite::Row) -> Result<User, rusqlite::Error> {
|
|
return Ok(User {
|
|
id: row.get(row.column_index("id")?)?,
|
|
session_id: row.get(row.column_index("session_id")?)?,
|
|
created: row.get(row.column_index("created")?)?,
|
|
last_active: row.get(row.column_index("last_active")?)?,
|
|
banned: row.get(row.column_index("banned")?)?,
|
|
moderator: row.get(row.column_index("moderator")?)?,
|
|
admin: row.get(row.column_index("admin")?)?,
|
|
});
|
|
}
|
|
}
|
|
|
|
fn as_opt_base64<S>(val: &Option<Vec<u8>>, s: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
s.serialize_str(&base64::encode(val.as_ref().unwrap()))
|
|
}
|
|
|
|
/// Old message structure returned by the deprecated compact_poll endpoint.
|
|
#[derive(Debug, Serialize)]
|
|
pub struct OldMessage {
|
|
/// Server-side message id. Migration: this becomes `id` in the new Message format.
|
|
pub server_id: i64,
|
|
/// Session id of the poster. Omitted when the information isn't available/useful (such as
|
|
/// submitting new messages). Migration: this becomes `session_id` in the new Message format.
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub public_key: Option<String>,
|
|
/// Timestamp, in unix epoch milliseconds. Migration: in the new Message format this value is
|
|
/// a floating point value (rather than integer) *and* is returned as actual unix time (i.e.
|
|
/// seconds) rather than milliseconds.
|
|
pub timestamp: i64,
|
|
/// Message data, encoded in base64
|
|
#[serde(serialize_with = "as_opt_base64")]
|
|
pub data: Option<Vec<u8>>,
|
|
/// XEd25519 message signature of the `data` bytes (not the base64 representation), encoded in
|
|
/// base64
|
|
#[serde(serialize_with = "as_opt_base64")]
|
|
pub signature: Option<Vec<u8>>,
|
|
}
|
|
|
|
impl OldMessage {
|
|
pub fn from_row(row: &rusqlite::Row) -> Result<OldMessage, rusqlite::Error> {
|
|
let mut data: Option<Vec<u8>> = row.get(row.column_index("data")?)?;
|
|
repad(&mut data, row.get::<_, Option<usize>>(row.column_index("data_size")?)?);
|
|
let session_id = match row.column_index("session_id") {
|
|
Ok(index) => Some(row.get(index)?),
|
|
Err(_) => None,
|
|
};
|
|
return Ok(OldMessage {
|
|
server_id: row.get(row.column_index("id")?)?,
|
|
public_key: session_id,
|
|
timestamp: (row.get::<_, f64>(row.column_index("posted")?)? * 1000.0) as i64,
|
|
data,
|
|
signature: row.get(row.column_index("signature")?)?,
|
|
});
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct Message {
|
|
/// The message id.
|
|
pub id: i64,
|
|
/// The session ID of the user who posted this message, in hex. Omitted in contexts where the
|
|
/// information isn't available or isn't useful, such as when inserting a message.
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub session_id: Option<String>,
|
|
/// unix timestamp of when the message was received on the server.
|
|
pub timestamp: f64,
|
|
/// unix timestamp of when the message was last edited (null if never edited).
|
|
pub edited: Option<f64>,
|
|
/// set to the room's current `updates` value at the time this message was created, last
|
|
/// edited, or deleted.
|
|
pub updated: i64,
|
|
/// The message data, encoded in base64. This field is omitted if the message is deleted.
|
|
#[serde(skip_serializing_if = "Option::is_none", serialize_with = "as_opt_base64")]
|
|
pub data: Option<Vec<u8>>,
|
|
/// The message signature, encoded in base64. This field is omitted if the message is deleted.
|
|
#[serde(skip_serializing_if = "Option::is_none", serialize_with = "as_opt_base64")]
|
|
pub signature: Option<Vec<u8>>,
|
|
/// Flag set to true if the message is deleted, and omitted otherwise.
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub deleted: Option<bool>,
|
|
}
|
|
|
|
fn repad(data: &mut Option<Vec<u8>>, size: Option<usize>) {
|
|
if let Some(size) = size {
|
|
if data.is_some() && data.as_ref().unwrap().len() < size {
|
|
data.as_mut().unwrap().resize(size, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Message {
|
|
pub fn from_row(row: &rusqlite::Row) -> Result<Message, rusqlite::Error> {
|
|
let mut data: Option<Vec<u8>> = row.get(row.column_index("data")?)?;
|
|
repad(&mut data, row.get::<_, Option<usize>>(row.column_index("data_size")?)?);
|
|
let deleted = if data.is_none() { Some(true) } else { None };
|
|
let session_id = match row.column_index("session_id") {
|
|
Ok(index) => Some(row.get(index)?),
|
|
Err(_) => None,
|
|
};
|
|
return Ok(Message {
|
|
id: row.get(row.column_index("id")?)?,
|
|
session_id,
|
|
timestamp: row.get(row.column_index("posted")?)?,
|
|
edited: row.get(row.column_index("edited")?)?,
|
|
updated: row.get(row.column_index("updated")?)?,
|
|
data,
|
|
signature: row.get(row.column_index("signature")?)?,
|
|
deleted,
|
|
});
|
|
}
|
|
}
|
|
|
|
fn bytes_from_base64<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
use serde::de::Error;
|
|
String::deserialize(deserializer)
|
|
.and_then(|str| base64::decode(&str).map_err(|err| Error::custom(err.to_string())))
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct PostMessage {
|
|
#[serde(deserialize_with = "bytes_from_base64")]
|
|
pub data: Vec<u8>,
|
|
#[serde(deserialize_with = "bytes_from_base64")]
|
|
pub signature: Vec<u8>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct DeletedMessage {
|
|
#[serde(rename = "id")]
|
|
pub updated: i64,
|
|
pub deleted_message_id: i64,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct Room {
|
|
#[serde(skip)]
|
|
pub id: i64,
|
|
#[serde(rename = "id")]
|
|
pub token: String,
|
|
pub name: String,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub description: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub image_file_id: Option<i64>,
|
|
pub created: f64,
|
|
pub updates: i64,
|
|
pub default_read: bool,
|
|
pub default_write: bool,
|
|
pub default_upload: bool,
|
|
}
|
|
|
|
impl Room {
|
|
pub fn from_row(row: &rusqlite::Row) -> Result<Room, rusqlite::Error> {
|
|
return Ok(Room {
|
|
id: row.get(row.column_index("id")?)?,
|
|
token: row.get(row.column_index("token")?)?,
|
|
name: row.get(row.column_index("name")?)?,
|
|
description: row.get(row.column_index("description")?)?,
|
|
image_file_id: row.get(row.column_index("image")?)?,
|
|
created: row.get(row.column_index("created")?)?,
|
|
updates: row.get(row.column_index("updates")?)?,
|
|
default_read: row.get(row.column_index("read")?)?,
|
|
default_write: row.get(row.column_index("write")?)?,
|
|
default_upload: row.get(row.column_index("upload")?)?,
|
|
});
|
|
}
|
|
}
|
|
|
|
// FIXME: this appears to be used for both add/remove. But what if we want to promote to admin, or
|
|
// demote to moderator?
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct ChangeModeratorRequestBody {
|
|
#[serde(rename = "room_id")]
|
|
pub room_token: String,
|
|
#[serde(rename = "public_key")]
|
|
pub session_id: String,
|
|
pub admin: Option<bool>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct PollRoomMetadata {
|
|
/// Token of the room to poll
|
|
pub room: String,
|
|
/// The last `info_update` value the client has; results are only returned if the room has been
|
|
/// modified since the value provided by the client.
|
|
pub since_update: i64,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct RoomDetails {
|
|
/// The token of this room
|
|
pub token: String,
|
|
/// Number of recently active users in the room
|
|
pub active_users: i64,
|
|
/// Metadata of the room; this omitted from the response when polling if the room metadata
|
|
/// (other than active user count) has not changed since the request update counter.
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub details: Option<RoomMetadata>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct RoomMetadata {
|
|
/// A counter that is updated whenever this room's metadata changes; clients are expected to
|
|
/// poll for updates using this id.
|
|
pub info_update: i64,
|
|
/// Unix timestamp (seconds since epoch) when this room was created
|
|
pub created: f64,
|
|
/// The human-readable room name
|
|
pub name: String,
|
|
/// Text description of the room
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub description: Option<String>,
|
|
/// ID of an uploaded file that contains the image for this room
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub image_id: Option<i64>,
|
|
/// IDs of any pinned message in this room.
|
|
pub pinned_messages: Vec<i64>,
|
|
/// List of non-admin public moderator Session IDs. This list includes both room-specific and
|
|
/// global moderators, but not admins and only if the moderator is configured as visible.
|
|
pub moderators: Vec<String>,
|
|
/// List of public admin session IDs for this room. In addition to everything moderators can
|
|
/// do, admins can also add/remove/ban other moderators and admins. As with `moderators` only
|
|
/// visible admins are included.
|
|
pub admins: Vec<String>,
|
|
/// List of hidden moderator Session IDs. This field is omitted if the requesting user is not
|
|
/// a moderator or admin.
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub hidden_mods: Option<Vec<String>>,
|
|
/// List of hidden admin Session IDs. This field is omitted if the requesting user is not a
|
|
/// moderator or admin.
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub hidden_admins: Option<Vec<String>>,
|
|
/// Will be present and true if the requesting user has moderator powers, omitted otherwise.
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub moderator: Option<bool>,
|
|
/// Will be present and true if the requesting user has admin powers, omitted otherwise.
|
|
pub admin: Option<bool>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct PollRoomMessages {
|
|
/// Token of the room to poll for messages.
|
|
pub room: String,
|
|
/// Return new messages, edit, and deletions posted since this `updates` value. Clients should
|
|
/// poll with the most recent updates value they have received.
|
|
pub since_update: i64,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct RoomMessages {
|
|
/// The token of this room
|
|
pub room: String,
|
|
/// Vector of new/edited/deleted message posted to the room since the requested update.
|
|
pub messages: Vec<Message>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct CompactPollRequestBody {
|
|
#[serde(rename = "room_id")]
|
|
pub room_token: String,
|
|
|
|
// Deprecated: older Session clients pass the authorization token through this. Newer clients
|
|
// should use signed requests instead.
|
|
pub auth_token: Option<String>,
|
|
|
|
// Input parameters to query. If these are omitted (or null) then this returns the latest 256
|
|
// messages/deletions, in reverse order from what you get with regular polling. New clients
|
|
// should update to the new polling endpoints ASAP.
|
|
pub from_message_server_id: Option<i64>,
|
|
pub from_deletion_server_id: Option<i64>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct CompactPollResponseBody {
|
|
#[serde(rename = "room_id")]
|
|
pub room_token: String,
|
|
pub status_code: u16,
|
|
pub deletions: Vec<DeletedMessage>,
|
|
pub messages: Vec<OldMessage>,
|
|
pub moderators: Vec<String>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
pub struct Challenge {
|
|
pub ciphertext: String,
|
|
pub ephemeral_public_key: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
pub struct StatusCode {
|
|
pub status_code: u16,
|
|
}
|