Implemented internal request function for client

added skeleton for other files
added todo
master
Edward Shen 2019-08-19 23:41:01 -04:00
parent dd15b606b8
commit 0e1b69f526
Signed by: edward
GPG Key ID: F350507060ED6C90
13 changed files with 986 additions and 25 deletions

6
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"cSpell.words": [
"errcode",
"reqwest"
]
}

View File

@ -7,3 +7,8 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
url = "2.1"
reqwest = "0.9"
serde_json = "1.0"
serde = "1.0"
olm-rs = "0.2"

442
docs/todo.md Normal file
View File

@ -0,0 +1,442 @@
# r0.5
- [ ] 2 API Standards
- [ ] 2.1 GET /_matrix/client/versions
- [x] ~~3 Web Browser Clients~~ *Not applicable*
- [ ] 4 Server Discovery
- [ ] 4.1 Well-known URI
- [ ] 4.1.1 GET /.well-known/matrix/client
- [ ] 5 Client Authentication
- [ ] 5.1 Using access tokens
- [ ] 5.2 Relationship between access tokens and devices
- [ ] 5.3 User-Interactive Authentication API
- [ ] 5.3.1 Overview
- [ ] 5.3.2 User-interactive API in the REST API
- [ ] 5.3.3 Example
- [ ] 5.3.4 Authentication types
- [ ] 5.3.4.1 Password-based
- [ ] 5.3.4.2 Google ReCaptcha
- [ ] 5.3.4.3 Token-based
- [ ] 5.3.4.4 OAuth2-based
- [ ] 5.3.4.5 Email-based (identity / homeserver)
- [ ] 5.3.4.6 Phone number/MSISDN-based (identity / homeserver)
- [ ] 5.3.4.7 Dummy Auth
- [ ] 5.3.5 Fallback
- [ ] 5.3.5.1 Example
- [ ] 5.3.6 Identifier types
- [ ] 5.3.6.1 Matrix User ID
- [ ] 5.3.6.2 Third-party ID
- [ ] 5.3.6.3 Phone number
- [ ] 5.4 Login
- [ ] 5.4.1 GET /_matrix/client/r0/login
- [ ] 5.4.2 POST /_matrix/client/r0/login
- [ ] 5.4.3 POST /_matrix/client/r0/logout
- [ ] 5.4.4 POST /_matrix/client/r0/logout/all
- [ ] 5.4.5 Login Fallback
- [ ] 5.5 Account registration and management
- [ ] 5.5.1 POST /_matrix/client/r0/register
- [ ] 5.5.2 POST /_matrix/client/r0/register/email/requestToken
- [ ] 5.5.3 POST /_matrix/client/r0/register/msisdn/requestToken
- [ ] 5.5.4 POST /_matrix/client/r0/account/password
- [ ] 5.5.5 POST /_matrix/client/r0/account/password/email/requestToken
- [ ] 5.5.6 POST /_matrix/client/r0/account/password/msisdn/requestToken
- [ ] 5.5.7 POST /_matrix/client/r0/account/deactivate
- [ ] 5.5.8 GET /_matrix/client/r0/register/available
- [ ] 5.5.9 Notes on password management
- [ ] 5.6 Adding Account Administrative Contact Information
- [ ] 5.6.1 GET /_matrix/client/r0/account/3pid
- [ ] 5.6.2 POST /_matrix/client/r0/account/3pid
- [ ] 5.6.3 POST /_matrix/client/r0/account/3pid/delete
- [ ] 5.6.4 POST /_matrix/client/r0/account/3pid/email/requestToken
- [ ] 5.6.5 POST /_matrix/client/r0/account/3pid/msisdn/requestToken
- [ ] 5.7 Current account information
- [ ] 5.7.1 GET /_matrix/client/r0/account/whoami
- [ ] 6 Capabilities negotiation
- [ ] 6.1 GET /_matrix/client/r0/capabilities
- [ ] 6.2 m.change_password capability
- [ ] 6.3 m.room_versions capability
- [ ] 7 Pagination
- [ ] 8 Filtering
- [ ] 8.1 Lazy-loading room members
- [ ] 8.2 API endpoints
- [ ] 8.2.1 POST /_matrix/client/r0/user/{userId}/filter
- [ ] 8.2.2 GET /_matrix/client/r0/user/{userId}/filter/{filterId}
- [ ] 9 Events
- [ ] 9.1 Types of room events
- [ ] 9.1.1 Event Fields
- [ ] 9.1.2 Room Event Fields
- [ ] 9.1.3 State Event Fields
- [ ] 9.2 Size limits
- [ ] 9.3 Room Events
- [ ] 9.3.1 m.room.aliases
- [ ] 9.3.2 m.room.canonical_alias
- [ ] 9.3.3 m.room.create
- [ ] 9.3.4 m.room.join_rules
- [ ] 9.3.5 m.room.member
- [ ] 9.3.6 m.room.power_levels
- [ ] 9.3.7 m.room.redaction
- [x] 9.4 Syncing
- [x] 9.4.1 GET /_matrix/client/r0/sync
- [x] ~~9.4.2 GET /_matrix/client/r0/events~~ *Deprecated*
- [x] ~~9.4.3 GET /_matrix/client/r0/initialSync~~ *Deprecated*
- [x] ~~9.4.4 GET /_matrix/client/r0/events/{eventId}~~ *Deprecated*
- [ ] 9.5 Getting events for a room
- [ ] 9.5.1 GET /_matrix/client/r0/rooms/{roomId}/event/{eventId}
- [ ] 9.5.2 GET /_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}
- [ ] 9.5.3 GET /_matrix/client/r0/rooms/{roomId}/state
- [ ] 9.5.4 GET /_matrix/client/r0/rooms/{roomId}/members
- [ ] 9.5.5 GET /_matrix/client/r0/rooms/{roomId}/joined_members
- [ ] 9.5.6 GET /_matrix/client/r0/rooms/{roomId}/messages
- [ ] 9.5.7 GET /_matrix/client/r0/rooms/{roomId}/initialSync
- [ ] 9.6 Sending events to a room
- [ ] 9.6.1 PUT /_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}
- [ ] 9.6.2 PUT /_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}
- [ ] 9.7 Redactions
- [ ] 9.7.1 Events
- [ ] 9.7.1.1 m.room.redaction
- [ ] 9.7.2 Client behaviour
- [ ] 9.7.2.1 PUT /_matrix/client/r0/rooms/{roomId}/redact/{eventId}/{txnId}
- [ ] 10 Rooms
- [ ] 10.1 Creation
- [ ] 10.1.1 POST /_matrix/client/r0/createRoom
- [ ] 10.2 Room aliases
- [ ] 10.2.1 PUT /_matrix/client/r0/directory/room/{roomAlias}
- [ ] 10.2.2 GET /_matrix/client/r0/directory/room/{roomAlias}
- [ ] 10.2.3 DELETE /_matrix/client/r0/directory/room/{roomAlias}
- [ ] 10.3 Permissions
- [ ] 10.4 Room membership
- [ ] 10.4.1 GET /_matrix/client/r0/joined_rooms
- [ ] 10.4.2 Joining rooms
- [ ] 10.4.2.1 POST /_matrix/client/r0/rooms/{roomId}/invite
- [ ] 10.4.2.2 POST /_matrix/client/r0/rooms/{roomId}/join
- [ ] 10.4.2.3 POST /_matrix/client/r0/join/{roomIdOrAlias}
- [ ] 10.4.3 Leaving rooms
- [ ] 10.4.3.1 POST /_matrix/client/r0/rooms/{roomId}/leave
- [ ] 10.4.3.2 POST /_matrix/client/r0/rooms/{roomId}/forget
- [ ] 10.4.3.3 POST /_matrix/client/r0/rooms/{roomId}/kick
- [ ] 10.4.4 Banning users in a room
- [ ] 10.4.4.1 POST /_matrix/client/r0/rooms/{roomId}/ban
- [ ] 10.4.4.2 POST /_matrix/client/r0/rooms/{roomId}/unban
- [ ] 10.5 Listing rooms
- [ ] 10.5.1 GET /_matrix/client/r0/directory/list/room/{roomId}
- [ ] 10.5.2 PUT /_matrix/client/r0/directory/list/room/{roomId}
- [ ] 10.5.3 GET /_matrix/client/r0/publicRooms
- [ ] 10.5.4 POST /_matrix/client/r0/publicRooms
- [ ] 11 User Data
- [ ] 11.1 User Directory
- [ ] 11.1.1 POST /_matrix/client/r0/user_directory/search
- [ ] 11.2 Profiles
- [ ] 11.2.1 PUT /_matrix/client/r0/profile/{userId}/displayname
- [ ] 11.2.2 GET /_matrix/client/r0/profile/{userId}/displayname
- [ ] 11.2.3 PUT /_matrix/client/r0/profile/{userId}/avatar_url
- [ ] 11.2.4 GET /_matrix/client/r0/profile/{userId}/avatar_url
- [ ] 11.2.5 GET /_matrix/client/r0/profile/{userId}
- [ ] 11.2.6 Events on Change of Profile Information
- [ ] 12 Security
- [ ] 12.1 Rate limiting
- [ ] 13 Modules
- [ ] 13.1 Feature Profiles
- [ ] 13.1.1 Summary
- [ ] 13.1.2 Clients
- [ ] 13.1.2.1 Stand-alone web (Web)
- [ ] 13.1.2.2 Mobile (Mobile)
- [ ] 13.1.2.3 Desktop (Desktop)
- [ ] 13.1.2.4 Command Line Interface (CLI)
- [ ] 13.1.2.5 Embedded (Embedded)
- [ ] 13.1.2.5.1 Application
- [ ] 13.1.2.5.2 Device
- [ ] 13.2 Instant Messaging
- [ ] 13.2.1 Events
- [ ] 13.2.1.1 m.room.message
- [ ] 13.2.1.2 m.room.message.feedback
- [ ] 13.2.1.3 m.room.name
- [ ] 13.2.1.4 m.room.topic
- [ ] 13.2.1.5 m.room.avatar
- [ ] 13.2.1.6 m.room.pinned_events
- [ ] 13.2.1.7 m.room.message msgtypes
- [ ] 13.2.1.7.1 m.text
- [ ] 13.2.1.7.2 m.emote
- [ ] 13.2.1.7.3 m.notice
- [ ] 13.2.1.7.4 m.image
- [ ] 13.2.1.7.5 m.file
- [ ] 13.2.1.7.6 m.audio
- [ ] 13.2.1.7.7 m.location
- [ ] 13.2.1.7.8 m.video
- [ ] 13.2.2 Client behaviour
- [ ] 13.2.2.1 Recommendations when sending messages
- [ ] 13.2.2.2 Local echo
- [ ] 13.2.2.3 Calculating the display name for a user
- [ ] 13.2.2.4 Displaying membership information with messages
- [ ] 13.2.2.5 Calculating the display name for a room
- [ ] 13.2.2.6 Forming relationships between events
- [ ] 13.2.2.6.1 Rich replies
- [ ] 13.2.2.6.1.1 Fallbacks and event representation
- [ ] 13.2.2.6.1.1.1 Stripping the fallback
- [ ] 13.2.2.6.1.1.2 Fallback for m.text, m.notice, and unrecognised message types
- [ ] 13.2.2.6.1.1.3 Fallback for m.emote
- [ ] 13.2.2.6.1.1.4 Fallback for m.image, m.video, m.audio, and m.file
- [ ] 13.2.3 Server behaviour
- [ ] 13.2.4 Security considerations
- [ ] 13.3 Voice over IP
- [ ] 13.3.1 Events
- [ ] 13.3.1.1 m.call.invite
- [ ] 13.3.1.2 m.call.candidates
- [ ] 13.3.1.3 m.call.answer
- [ ] 13.3.1.4 m.call.hangup
- [ ] 13.3.2 Client behaviour
- [ ] 13.3.2.1 Glare
- [ ] 13.3.3 Server behaviour
- [ ] 13.3.3.1 GET /_matrix/client/r0/voip/turnServer
- [ ] 13.3.4 Security considerations
- [ ] 13.4 Typing Notifications
- [ ] 13.4.1 Events
- [ ] 13.4.1.1 m.typing
- [ ] 13.4.2 Client behaviour
- [ ] 13.4.2.1 PUT /_matrix/client/r0/rooms/{roomId}/typing/{userId}
- [ ] 13.4.3 Security considerations
- [ ] 13.5 Receipts
- [ ] 13.5.1 Events
- [ ] 13.5.1.1 m.receipt
- [ ] 13.5.2 Client behaviour
- [ ] 13.5.2.1 POST /_matrix/client/r0/rooms/{roomId}/receipt/{receiptType}/{eventId}
- [ ] 13.5.3 Server behaviour
- [ ] 13.5.4 Security considerations
- [ ] 13.6 Fully read markers
- [ ] 13.6.1 Events
- [ ] 13.6.1.1 m.fully_read
- [ ] 13.6.2 Client behaviour
- [ ] 13.6.2.1 POST /_matrix/client/r0/rooms/{roomId}/read_markers
- [ ] 13.6.3 Server behaviour
- [ ] 13.7 Presence
- [ ] 13.7.1 Events
- [ ] 13.7.1.1 m.presence
- [ ] 13.7.2 Client behaviour
- [ ] 13.7.2.1 PUT /_matrix/client/r0/presence/{userId}/status
- [ ] 13.7.2.2 GET /_matrix/client/r0/presence/{userId}/status
- [ ] 13.7.2.3 Last active ago
- [ ] 13.7.2.4 Idle timeout
- [ ] 13.7.3 Security considerations
- [ ] 13.8 Content repository
- [ ] 13.8.1 Matrix Content (MXC) URIs
- [ ] 13.8.2 Client behaviour
- [ ] 13.8.2.1 POST /_matrix/media/r0/upload
- [ ] 13.8.2.2 GET /_matrix/media/r0/download/{serverName}/{mediaId}
- [ ] 13.8.2.3 GET /_matrix/media/r0/download/{serverName}/{mediaId}/{fileName}
- [ ] 13.8.2.4 GET /_matrix/media/r0/thumbnail/{serverName}/{mediaId}
- [ ] 13.8.2.5 GET /_matrix/media/r0/preview_url
- [ ] 13.8.2.6 GET /_matrix/media/r0/config
- [ ] 13.8.2.7 Thumbnails
- [ ] 13.8.3 Security considerations
- [ ] 13.9 Send-to-Device messaging
- [ ] 13.9.1 Client behaviour
- [ ] 13.9.2 Server behaviour
- [ ] 13.9.3 Protocol definitions
- [ ] 13.9.3.1 PUT /_matrix/client/r0/sendToDevice/{eventType}/{txnId}
- [ ] 13.9.3.2 Extensions to /sync
- [ ] 13.10 Device Management
- [ ] 13.10.1 Client behaviour
- [ ] 13.10.1.1 GET /_matrix/client/r0/devices
- [ ] 13.10.1.2 GET /_matrix/client/r0/devices/{deviceId}
- [ ] 13.10.1.3 PUT /_matrix/client/r0/devices/{deviceId}
- [ ] 13.10.1.4 DELETE /_matrix/client/r0/devices/{deviceId}
- [ ] 13.10.1.5 POST /_matrix/client/r0/delete_devices
- [ ] 13.10.2 Security considerations
- [ ] 13.11 End-to-End Encryption
- [ ] 13.11.1 Key Distribution
- [ ] 13.11.1.1 Overview
- [ ] 13.11.1.2 Key algorithms
- [ ] 13.11.1.3 Device keys
- [ ] 13.11.1.4 Uploading keys
- [ ] 13.11.1.5 Tracking the device list for a user
- [ ] 13.11.1.6 Sending encrypted attachments
- [ ] 13.11.1.6.1 Extensions to m.message msgtypes
- [ ] 13.11.1.7 Claiming one-time keys
- [ ] 13.11.2 Device verification
- [ ] 13.11.2.1 Key verification framework
- [ ] 13.11.2.1.1 m.key.verification.request
- [ ] 13.11.2.1.2 m.key.verification.start
- [ ] 13.11.2.1.3 m.key.verification.cancel
- [ ] 13.11.2.2 Short Authentication String (SAS) verification
- [ ] 13.11.2.2.1 Error and exception handling
- [ ] 13.11.2.2.2 Verification messages specific to SAS
- [ ] 13.11.2.2.3 m.key.verification.start
- [ ] 13.11.2.2.4 m.key.verification.accept
- [ ] 13.11.2.2.5 m.key.verification.key
- [ ] 13.11.2.2.6 m.key.verification.mac
- [ ] 13.11.2.2.7 HKDF calculation
- [ ] 13.11.2.2.8 SAS method: decimal
- [ ] 13.11.2.2.9 SAS method: emoji
- [ ] 13.11.3 Sharing keys between devices
- [ ] 13.11.3.1 Key requests
- [ ] 13.11.3.2 Key exports
- [ ] 13.11.3.2.1 Key export format
- [ ] 13.11.4 Messaging Algorithms
- [ ] 13.11.4.1 Messaging Algorithm Names
- [ ] 13.11.4.2 m.olm.v1.curve25519-aes-sha2
- [ ] 13.11.4.2.1 Recovering from undecryptable messages
- [ ] 13.11.4.3 m.megolm.v1.aes-sha2
- [ ] 13.11.5 Protocol definitions
- [ ] 13.11.5.1 Events
- [ ] 13.11.5.1.1 m.room.encryption
- [ ] 13.11.5.1.2 m.room.encrypted
- [ ] 13.11.5.1.3 m.room_key
- [ ] 13.11.5.1.4 m.room_key_request
- [ ] 13.11.5.1.5 m.forwarded_room_key
- [ ] 13.11.5.1.6 m.dummy
- [ ] 13.11.5.2 Key management API
- [ ] 13.11.5.2.1 POST /_matrix/client/r0/keys/upload
- [ ] 13.11.5.2.2 POST /_matrix/client/r0/keys/query
- [ ] 13.11.5.2.3 POST /_matrix/client/r0/keys/claim
- [ ] 13.11.5.2.4 GET /_matrix/client/r0/keys/changes
- [ ] 13.11.5.3 Extensions to /sync
- [ ] 13.12 Room History Visibility
- [ ] 13.12.1 Events
- [ ] 13.12.1.1 m.room.history_visibility
- [ ] 13.12.2 Client behaviour
- [ ] 13.12.3 Server behaviour
- [ ] 13.12.4 Security considerations
- [ ] 13.13 Push Notifications
- [ ] 13.13.1 Client behaviour
- [ ] 13.13.1.1 GET /_matrix/client/r0/pushers
- [ ] 13.13.1.2 POST /_matrix/client/r0/pushers/set
- [ ] 13.13.1.3 Listing Notifications
- [ ] 13.13.1.3.1 GET /_matrix/client/r0/notifications
- [ ] 13.13.1.4 Receiving notifications
- [ ] 13.13.1.5 Push Rules
- [ ] 13.13.1.5.1 Actions
- [ ] 13.13.1.5.1.1 Tweaks
- [ ] 13.13.1.5.2 Predefined Rules
- [ ] 13.13.1.5.2.1 Default Override Rules
- [ ] 13.13.1.5.2.1.1 .m.rule.master
- [ ] 13.13.1.5.2.1.2 .m.rule.suppress_notices
- [ ] 13.13.1.5.2.1.3 .m.rule.invite_for_me
- [ ] 13.13.1.5.2.1.4 .m.rule.member_event
- [ ] 13.13.1.5.2.1.5 .m.rule.contains_display_name
- [ ] 13.13.1.5.2.1.6 .m.rule.tombstone
- [ ] 13.13.1.5.2.1.7 .m.rule.roomnotif
- [ ] 13.13.1.5.2.2 Default Content Rules
- [ ] 13.13.1.5.2.2.1 .m.rule.contains_user_name
- [ ] 13.13.1.5.2.3 Default Underride Rules
- [ ] 13.13.1.5.2.3.1 .m.rule.call
- [ ] 13.13.1.5.2.3.2 .m.rule.encrypted_room_one_to_one
- [ ] 13.13.1.5.2.3.3 .m.rule.room_one_to_one
- [ ] 13.13.1.5.2.3.4 .m.rule.message
- [ ] 13.13.1.5.2.3.5 .m.rule.encrypted
- [ ] 13.13.1.5.3 Conditions
- [ ] 13.13.1.6 Push Rules: API
- [ ] 13.13.1.6.1 GET /_matrix/client/r0/pushrules/
- [ ] 13.13.1.6.2 GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}
- [ ] 13.13.1.6.3 DELETE /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}
- [ ] 13.13.1.6.4 PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}
- [ ] 13.13.1.6.5 GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/enabled
- [ ] 13.13.1.6.6 PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/enabled
- [ ] 13.13.1.6.7 GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/actions
- [ ] 13.13.1.6.8 PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/actions
- [ ] 13.13.1.7 Push Rules: Events
- [ ] 13.13.1.7.1 m.push_rules
- [ ] 13.13.1.7.2 Examples
- [ ] 13.13.2 Server behaviour
- [ ] 13.13.3 Push Gateway behaviour
- [ ] 13.13.3.1 Recommendations for APNS
- [ ] 13.13.4 Security considerations
- [ ] 13.14 Third party invites
- [ ] 13.14.1 Events
- [ ] 13.14.1.1 m.room.third_party_invite
- [ ] 13.14.2 Client behaviour
- [ ] 13.14.2.1 POST /_matrix/client/r0/rooms/{roomId}/invite
- [ ] 13.14.3 Server behaviour
- [ ] 13.14.4 Security considerations
- [ ] 13.15 Server Side Search
- [ ] 13.15.1 Client behaviour
- [ ] 13.15.1.1 POST /_matrix/client/r0/search
- [ ] 13.15.2 Search Categories
- [ ] 13.15.2.1 room_events
- [ ] 13.15.3 Ordering
- [ ] 13.15.4 Groups
- [ ] 13.15.5 Pagination
- [ ] 13.15.6 Security considerations
- [ ] 13.16 Guest Access
- [ ] 13.16.1 Events
- [ ] 13.16.1.1 m.room.guest_access
- [ ] 13.16.2 Client behaviour
- [ ] 13.16.3 Server behaviour
- [ ] 13.16.4 Security considerations
- [ ] 13.17 Room Previews
- [ ] 13.17.1 Client behaviour
- [ ] 13.17.1.1 GET /_matrix/client/r0/events
- [ ] 13.17.2 Server behaviour
- [ ] 13.17.3 Security considerations
- [ ] 13.18 Room Tagging
- [ ] 13.18.1 Events
- [ ] 13.18.1.1 m.tag
- [ ] 13.18.2 Client Behaviour
- [ ] 13.18.2.1 GET /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags
- [ ] 13.18.2.2 PUT /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags/{tag}
- [ ] 13.18.2.3 DELETE /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags/{tag}
- [ ] 13.19 Client Config
- [ ] 13.19.1 Events
- [ ] 13.19.2 Client Behaviour
- [ ] 13.19.2.1 PUT /_matrix/client/r0/user/{userId}/account_data/{type}
- [ ] 13.19.2.2 GET /_matrix/client/r0/user/{userId}/account_data/{type}
- [ ] 13.19.2.3 PUT /_matrix/client/r0/user/{userId}/rooms/{roomId}/account_data/{type}
- [ ] 13.19.2.4 GET /_matrix/client/r0/user/{userId}/rooms/{roomId}/account_data/{type}
- [ ] 13.19.3 Server Behaviour
- [ ] 13.20 Server Administration
- [ ] 13.20.1 Client Behaviour
- [ ] 13.20.1.1 GET /_matrix/client/r0/admin/whois/{userId}
- [ ] 13.21 Event Context
- [ ] 13.21.1 Client behaviour
- [ ] 13.21.1.1 GET /_matrix/client/r0/rooms/{roomId}/context/{eventId}
- [ ] 13.21.2 Security considerations
- [ ] 13.22 SSO client login
- [ ] 13.22.1 Client behaviour
- [ ] 13.22.1.1 GET /_matrix/client/r0/login/sso/redirect
- [ ] 13.22.2 Server behaviour
- [ ] 13.22.2.1 Handling the redirect endpoint
- [ ] 13.22.2.2 Handling the authentication endpoint
- [ ] 13.23 Direct Messaging
- [ ] 13.23.1 Events
- [ ] 13.23.1.1 m.direct
- [ ] 13.23.2 Client behaviour
- [ ] 13.23.3 Server behaviour
- [ ] 13.24 Ignoring Users
- [ ] 13.24.1 Events
- [ ] 13.24.1.1 m.ignored_user_list
- [ ] 13.24.2 Client behaviour
- [ ] 13.24.3 Server behaviour
- [ ] 13.25 Sticker Messages
- [ ] 13.25.1 Events
- [ ] 13.25.1.1 m.sticker
- [ ] 13.25.2 Client behaviour
- [ ] 13.26 Reporting Content
- [ ] 13.26.1 Client behaviour
- [ ] 13.26.1.1 POST /_matrix/client/r0/rooms/{roomId}/report/{eventId}
- [ ] 13.26.2 Server behaviour
- [ ] 13.27 Third Party Networks
- [ ] 13.27.1 Third Party Lookups
- [ ] 13.27.1.1 GET /_matrix/client/r0/thirdparty/protocols
- [ ] 13.27.1.2 GET /_matrix/client/r0/thirdparty/protocol/{protocol}
- [ ] 13.27.1.3 GET /_matrix/client/r0/thirdparty/location/{protocol}
- [ ] 13.27.1.4 GET /_matrix/client/r0/thirdparty/user/{protocol}
- [ ] 13.27.1.5 GET /_matrix/client/r0/thirdparty/location
- [ ] 13.27.1.6 GET /_matrix/client/r0/thirdparty/user
- [ ] 13.28 OpenID
- [ ] 13.28.1 POST /_matrix/client/r0/user/{userId}/openid/request_token
- [ ] 13.29 Server Access Control Lists (ACLs) for rooms
- [ ] 13.29.1 m.room.server_acl
- [ ] 13.29.2 Client behaviour
- [ ] 13.29.3 Server behaviour
- [ ] 13.29.4 Security considerations
- [ ] 13.30 User, room, and group mentions
- [ ] 13.30.1 Client behaviour
- [ ] 13.31 Room Upgrades
- [ ] 13.31.1 Events
- [ ] 13.31.1.1 m.room.tombstone
- [ ] 13.31.2 Client behaviour
- [ ] 13.31.2.1 POST /_matrix/client/r0/rooms/{roomId}/upgrade
- [ ] 13.31.3 Server behaviour
- [ ] 13.32 Server Notices
- [ ] 13.32.1 Events
- [ ] 13.32.1.1 m.room.message (m.server_notice)
- [ ] 13.32.2 Client behaviour
- [ ] 13.32.3 Server behaviour

231
src/api/client.rs Normal file
View File

@ -0,0 +1,231 @@
use crate::api::methods::sync::SyncResponse;
use reqwest::Client as reqwest_client;
use reqwest::{
header::{HeaderMap, HeaderValue, CONTENT_TYPE, USER_AGENT},
StatusCode,
};
use std::{collections::HashMap, error::Error, fmt, time};
use url::{ParseError, Url};
const V2_API_PATH: &str = "/_matrix/client/r0";
#[derive(Debug)]
pub enum MatrixParseError {
ParseError(ParseError),
EmptyScheme,
}
pub enum PresenceState {
Offline,
Online,
Unavailable,
}
impl From<ParseError> for MatrixParseError {
fn from(parse_error: ParseError) -> Self {
MatrixParseError::ParseError(parse_error)
}
}
#[derive(Debug)]
pub struct ResponseError {
code: StatusCode,
content: String,
}
impl fmt::Display for ResponseError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "{}: {}", self.code, self.content)
}
}
impl Error for ResponseError {}
pub enum MatrixHTTPMethod {
Get,
Put,
Delete,
Post,
}
#[derive(Debug)]
pub struct Client {
homeserver_url: String,
access_token: Option<String>,
mxid: Option<String>,
default_492_wait_ms: u64,
use_auth_header: bool,
reqwest_client: reqwest_client,
}
impl Client {
pub fn new(
homeserver_url: &str,
access_token: Option<String>,
mxid: Option<String>,
default_492_wait_ms: Option<u64>,
use_auth_header: Option<bool>,
) -> Result<Self, MatrixParseError> {
let url = Url::parse(homeserver_url)?;
if url.scheme().is_empty() {
return Err(MatrixParseError::EmptyScheme);
}
Ok(Client {
homeserver_url: homeserver_url.to_string(),
access_token,
mxid,
default_492_wait_ms: default_492_wait_ms.unwrap_or_else(|| 5000),
use_auth_header: use_auth_header.unwrap_or_else(|| true),
reqwest_client: reqwest_client::new(),
})
}
/// Sends an API request to the homeserver using the specified method and
/// path, returning either an error or the response text.
///
/// The header will automatically be populated with a user agent and have
/// the content type set to `application/json`. If a token was provided, it
/// will be used as a bearer auth header or as a query.
///
/// This is a blocking, synchronous send. If the response from the
/// homeserver indicates that too many requests were sent, it will attempt
/// to wait the specified duration (or a provided default) before retrying.
fn send(
&self,
method: MatrixHTTPMethod,
path: Option<&str>,
content: Option<String>,
query_params: Option<HashMap<String, String>>,
headers: Option<HeaderMap>,
) -> Result<String, Box<dyn std::error::Error>> {
let mut query_params = query_params.unwrap_or_default();
let mut headers = headers.unwrap_or_default();
let endpoint = &format!(
"{}{}",
self.homeserver_url,
path.unwrap_or_else(|| V2_API_PATH),
);
let mut request = match method {
MatrixHTTPMethod::Get => self.reqwest_client.get(endpoint),
MatrixHTTPMethod::Put => self.reqwest_client.put(endpoint),
MatrixHTTPMethod::Delete => self.reqwest_client.delete(endpoint),
MatrixHTTPMethod::Post => self.reqwest_client.post(endpoint),
};
if !headers.contains_key(&USER_AGENT) {
let user_agent = &format!("libmatrix-client/{}", env!("CARGO_PKG_VERSION"));
headers.insert(USER_AGENT, HeaderValue::from_str(user_agent)?);
}
if !headers.contains_key(&CONTENT_TYPE) {
headers.insert(CONTENT_TYPE, HeaderValue::from_str("application/json")?);
}
if let Some(token) = &self.access_token {
if self.use_auth_header {
request = request.bearer_auth(token);
} else {
query_params.insert("access_token".to_string(), token.to_string());
}
}
if let Some(id) = &self.mxid {
query_params.insert("user_id".to_string(), id.to_string());
}
request = request.headers(headers).query(&query_params);
if let Some(content) = content {
request = request.body(content);
}
loop {
let mut res = request
.try_clone()
.expect("Unable to clone request")
.send()?;
if res.status().is_success() {
return Ok(res.text()?);
} else if res.status() == StatusCode::TOO_MANY_REQUESTS {
let mut body: HashMap<String, String> = res.json()?;
if let Some(value) = body.get("error") {
body = serde_json::from_str(value)?;
}
if let Some(value) = body.get("retry_after_ms") {
std::thread::sleep(time::Duration::from_millis(value.parse::<u64>()?));
} else {
std::thread::sleep(time::Duration::from_millis(self.default_492_wait_ms));
}
} else {
return Err(Box::from(ResponseError {
code: res.status(),
content: res.text()?,
}));
}
}
}
fn send_query(
&self,
method: MatrixHTTPMethod,
path: &str,
query_params: HashMap<String, String>,
) -> Result<String, Box<dyn Error>> {
self.send(method, Some(path), None, Some(query_params), None)
}
pub fn sync(
&self,
bookmark_token: Option<&str>,
timeout_ms: Option<u64>,
filter: Option<&str>,
get_full_state: Option<bool>,
set_presence: Option<PresenceState>,
) -> Result<SyncResponse, Box<dyn Error>> {
let mut params: HashMap<String, String> = HashMap::with_capacity(5);
params.insert(
"timeout".to_string(),
timeout_ms.unwrap_or_else(|| 30000).to_string(),
);
if let Some(token) = bookmark_token {
params.insert("since".to_string(), token.to_string());
}
if let Some(filter) = filter {
params.insert("filter".to_string(), filter.to_string());
}
if let Some(true) = get_full_state {
params.insert("full_state".to_string(), "true".to_string());
}
params.insert(
"full_state".to_string(),
match set_presence {
Some(PresenceState::Online) => "online",
Some(PresenceState::Unavailable) => "unavailable",
None | Some(PresenceState::Offline) => "offline",
}
.to_string(),
);
Ok(serde_json::from_str(&self.send_query(
MatrixHTTPMethod::Get,
"/sync",
params,
)?)?)
}
}
#[derive(Default)]
pub struct ApiError {}
impl fmt::Display for ApiError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "an error occurred!")
}
}

86
src/api/error.rs Normal file
View File

@ -0,0 +1,86 @@
use serde::Deserialize;
use std::collections::HashMap;
// Various helper constants for common error codes.
pub const FORBIDDEN: &str = "M_FORBIDDEN";
pub const UNKNOWN_TOKEN: &str = "M_UNKNOWN_TOKEN";
pub const MISSING_TOKEN: &str = "M_MISSING_TOKEN";
pub const BAD_JSON: &str = "M_BAD_JSON";
pub const NOT_JSON: &str = "M_NOT_JSON";
pub const NOT_FOUND: &str = "M_NOT_FOUND";
pub const LIMIT_EXCEEDED: &str = "M_LIMIT_EXCEEDED";
pub const UNKNOWN: &str = "M_UNKNOWN";
pub const UNRECOGNIZED: &str = "M_UNRECOGNIZED";
pub const UNAUTHORIZED: &str = "M_UNAUTHORIZED";
pub const USER_IN_USE: &str = "M_USER_IN_USE";
pub const INVALID_USERNAME: &str = "M_INVALID_USERNAME";
pub const IN_USE: &str = "M_ROOM_IN_USE";
pub const STATE: &str = "M_INVALID_ROOM_STATE";
pub const THREEPID_IN_USE: &str = "M_THREEPID_IN_USE";
pub const THREEPID_NOT_FOUND: &str = "M_THREEPID_NOT_FOUND";
pub const THREEPID_AUTH_FAILED: &str = "M_THREEPID_AUTH_FAILED";
pub const THREEPID_DENIED: &str = "M_THREEPID_DENIED";
pub const SERVER_NOT_TRUSTED: &str = "M_SERVER_NOT_TRUSTED";
pub const UNSUPPORTED_ROOM_VERSION: &str = "M_UNSUPPORTED_ROOM_VERSION";
pub const INCOMPATIBLE_ROOM_VERSION: &str = "M_INCOMPATIBLE_ROOM_VERSION";
pub const BAD_STATE: &str = "M_BAD_STATE";
pub const GUEST_ACCESS_FORBIDDEN: &str = "M_GUEST_ACCESS_FORBIDDEN";
pub const CAPTCHA_NEEDED: &str = "M_CAPTCHA_NEEDED";
pub const CAPTCHA_INVALID: &str = "M_CAPTCHA_INVALID";
pub const MISSING_PARAM: &str = "M_MISSING_PARAM";
pub const INVALID_PARAM: &str = "M_INVALID_PARAM";
pub const TOO_LARGE: &str = "M_TOO_LARGE";
pub const EXCLUSIVE: &str = "M_EXCLUSIVE";
pub const RESOURCE_LIMIT_EXCEEDED: &str = "M_RESOURCE_LIMIT_EXCEEDED";
pub const CANNOT_LEAVE_SERVER_NOTICE_ROOM: &str = "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM";
/// Implements the standard error response as defined by [Section 2](https://matrix.org/docs/spec/client_server/r0.5.0#api-standards)
/// of the Client-Server API standard as a HashMap. We don't have a better way
/// of representing this class, since we need to conform to having different
/// fields for different errors.
#[derive(Deserialize)]
pub struct ApiError {
#[serde(flatten)]
pub data: HashMap<String, String>,
}
impl ApiError {
/// Helper function for obtaining the error code. Access the `errcode`
/// field, which must be present on all valid error messages.
pub fn error_code(&self) -> &str {
self.data
.get("errcode")
.expect("Error did not contain required errcode field")
}
/// Helper function for obtaining the error message, which is a
/// human-readable field that explains the issue. This accesses the `error`
/// field, which must be present on all valid error messages.
pub fn error_message(&self) -> &str {
self.data
.get("error")
.expect("Error did not contain required error field")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deserialize_basic_error_msg() {
let test_msg = r#"{"errcode": "M_FORBIDDEN", "error": "hello"}"#;
let deserialized: ApiError = serde_json::from_str(test_msg).unwrap();
assert_eq!(deserialized.error_code(), FORBIDDEN);
assert_eq!(deserialized.error_message(), "hello");
}
#[test]
fn deserialize_error_with_custom_field() {
let test_msg = r#"{"errcode": "M_UNKNOWN", "error": "foo", "a": "b"}"#;
let deserialized: ApiError = serde_json::from_str(test_msg).unwrap();
assert_eq!(deserialized.error_code(), UNKNOWN);
assert_eq!(deserialized.error_message(), "foo");
assert_eq!(deserialized.data.get("a").unwrap(), "b");
}
}

1
src/api/methods/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod sync;

169
src/api/methods/sync.rs Normal file
View File

@ -0,0 +1,169 @@
use serde::Deserialize;
use std::collections::HashMap;
#[derive(Deserialize)]
pub struct SyncResponse {
pub next_batch: String,
pub rooms: Option<Rooms>,
pub presence: Option<Presence>,
pub account_data: Option<AccountData>,
pub to_device: Option<ToDevice>,
pub device_lists: Option<DeviceLists>,
pub device_one_time_keys_count: Option<HashMap<String, u64>>,
}
#[derive(Deserialize)]
pub struct Rooms {
pub join: Option<HashMap<String, JoinedRoom>>,
pub invite: Option<HashMap<String, InvitedRoom>>,
pub leave: Option<HashMap<String, LeftRoom>>,
}
#[derive(Deserialize)]
pub struct JoinedRoom {
pub summary: Option<RoomSummary>,
pub state: Option<State>,
pub timeline: Option<Timeline>,
pub ephemeral: Option<Ephemeral>,
pub account_date: Option<AccountData>,
pub unread_notifications: Option<UnreadNotificationCounts>,
}
#[derive(Deserialize)]
pub struct RoomSummary {
#[serde(rename = "m.heroes")]
pub heros: Option<Vec<String>>,
#[serde(rename = "m.joined_member_count")]
pub joined_member_count: Option<u64>,
#[serde(rename = "m.invited_member_count")]
pub invited_member_count: Option<u64>,
}
#[derive(Deserialize)]
pub struct State {
pub events: Option<StateEvents>,
}
#[derive(Deserialize)]
pub struct StateEvents {
pub content: serde_json::value::Value, // This is an json object
pub r#type: String,
pub event_id: String,
pub sender: String,
pub origin_server_ts: u64,
pub unsigned: Option<UnsignedData>,
pub prev_content: Option<EventContent>,
pub state_key: String,
}
#[derive(Deserialize)]
pub struct UnsignedData {
pub age: Option<u64>,
pub redacted_because: Option<Event>,
pub transaction_id: Option<String>,
}
#[derive(Deserialize)]
pub struct Event {
pub content: serde_json::value::Value, // Json object
pub r#type: String,
}
#[derive(Deserialize)]
pub struct EventContent {
pub avatar_url: Option<String>,
pub displayname: Option<String>, //type is string or null????
pub membership: Membership,
pub state_key: String,
}
#[derive(Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Membership {
Invite,
Join,
Knock,
Leave,
Ban,
}
#[derive(Deserialize)]
pub struct Timeline {
pub events: Option<Vec<RoomEvent>>,
pub limited: Option<bool>,
pub prev_batch: Option<String>,
}
#[derive(Deserialize)]
pub struct RoomEvent {
pub content: String,
pub r#type: String,
pub event_id: String,
pub sender: String,
pub origin_server_ts: u64,
pub unsigned: Option<UnsignedData>,
}
#[derive(Deserialize)]
pub struct Ephemeral {
pub events: Option<Vec<Event>>,
}
#[derive(Deserialize)]
pub struct AccountData {
pub events: Option<Vec<Event>>,
}
#[derive(Deserialize)]
pub struct UnreadNotificationCounts {
pub highlight_count: Option<u64>,
pub notification_count: Option<u64>,
}
#[derive(Deserialize)]
pub struct InvitedRoom {
pub invite_state: Option<InviteState>,
}
#[derive(Deserialize)]
pub struct InviteState {
pub events: Option<Vec<StrippedState>>,
}
#[derive(Deserialize)]
pub struct StrippedState {
pub content: EventContent,
pub state_key: String,
pub r#type: String,
pub sender: String,
}
#[derive(Deserialize)]
pub struct LeftRoom {
pub state: Option<State>,
pub timeline: Option<Timeline>,
pub account_data: Option<AccountData>,
}
#[derive(Deserialize)]
pub struct Presence {
pub events: Option<Vec<Event>>,
}
#[derive(Deserialize)]
pub struct ToDevice {
pub events: Option<Vec<ToDeviceEvent>>,
}
#[derive(Deserialize)]
pub struct ToDeviceEvent {
pub content: Option<EventContent>,
pub sender: Option<String>,
pub r#type: Option<String>,
}
#[derive(Deserialize)]
pub struct DeviceLists {
pub changed: Option<Vec<String>>,
pub left: Option<Vec<String>>,
}

View File

@ -1,19 +1,14 @@
use std::fmt;
use serde::Deserialize;
use std::collections::HashMap;
#[derive(Debug, PartialEq)]
pub struct Client {}
pub mod client;
pub mod error;
pub mod methods;
impl Client{
pub fn new() -> Self {
Client {}
}
}
#[derive(Default)]
pub struct ApiError {}
impl fmt::Display for ApiError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "an error occurred!")
}
pub type Result<T> = std::result::Result<T, error::ApiError>;
#[derive(Deserialize)]
pub struct VersionApiResp {
pub versions: Vec<String>,
pub unstable_features: HashMap<String, bool>,
}

3
src/api/rooms.rs Normal file
View File

@ -0,0 +1,3 @@
trait RoomApi {
fn send_state_event(room_id: &str, )
}

0
src/api/util.rs Normal file
View File

View File

@ -1,6 +1,7 @@
#![forbid(unsafe_code)]
// #![warn(missing_docs)]
mod api;
pub mod api;
mod client;
mod room;
mod user;

0
src/room/events.rs Normal file
View File

View File

@ -1,4 +1,4 @@
use crate::api::{ApiError, Client};
use crate::api::client::{ApiError, Client};
use crate::room::Room;
use std::fmt;
@ -29,13 +29,19 @@ impl fmt::Display for UserInitError {
}
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
struct User {
id: String,
display_name: Option<String>,
client: Client,
}
impl PartialEq for User {
fn eq(&self, rhs: &Self) -> bool {
self.id == rhs.id && self.display_name == rhs.display_name
}
}
impl User {
/// Constructs a new User. This represents a user in a room.
///
@ -46,7 +52,11 @@ impl User {
///
/// Returns either a new User struct or an error containing the reason why it failed to create a
/// new User.
pub fn new(id: String, display_name: Option<String>) -> Result<Self, UserInitError> {
pub fn new(
client: Client,
id: String,
display_name: Option<String>,
) -> Result<Self, UserInitError> {
if !id.starts_with("@") {
Err(UserInitError::new(
"User ID must start with a @",
@ -61,7 +71,7 @@ impl User {
Ok(User {
id,
display_name,
client: Client::new(),
client,
})
}
}
@ -90,7 +100,11 @@ mod tests {
#[test]
fn new_returns_err_on_invalid_id() {
assert_eq!(
User::new(String::from("abc:edf"), None),
User::new(
Client::new("https://google.com", None, None, None, None).unwrap(),
String::from("abc:edf"),
None
),
Err(UserInitError {
message: "User ID must start with a @".to_string(),
reason: UserInitErrorReason::InvalidUsername
@ -98,7 +112,11 @@ mod tests {
);
assert_eq!(
User::new(String::from("@abcedf"), None),
User::new(
Client::new("https://google.com", None, None, None, None).unwrap(),
String::from("@abcedf"),
None
),
Err(UserInitError {
message: "User ID must contain a :".to_string(),
reason: UserInitErrorReason::NoDomainProvided
@ -109,11 +127,15 @@ mod tests {
#[test]
fn new_returns_struct_on_valid_input() {
assert_eq!(
User::new("@eddie:eddie.sh".to_string(), None),
User::new(
Client::new("https://google.com", None, None, None, None).unwrap(),
"@eddie:eddie.sh".to_string(),
None
),
Ok(User {
id: "@eddie:eddie.sh".to_string(),
display_name: None,
client: Client::new()
client: Client::new("https://google.com", None, None, None, None).unwrap()
})
)
}