diff --git a/Cargo.lock b/Cargo.lock
index e9a49ddf9ae0fcca27743e660521e5c9057cd551..d00b6a733371b98e5257110af5aace21fbb9a2c1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2351,7 +2351,7 @@ dependencies = [
 
 [[package]]
 name = "revolt"
-version = "0.3.1"
+version = "0.3.2"
 dependencies = [
  "async-std",
  "async-tungstenite",
diff --git a/Cargo.toml b/Cargo.toml
index bd44ca9c69a4bc8993794428f80cc6990779865f..08a29907898cf30bff2b338f0ca7a8cc990edbf0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "revolt"
-version = "0.3.1"
+version = "0.3.2"
 authors = ["Paul Makles <paulmakles@gmail.com>"]
 edition = "2018"
 
diff --git a/src/database/entities/channel.rs b/src/database/entities/channel.rs
index 3ee927a122ba196b9573c3dfef407234d5c37429..359fe379027f7fa13e2cd5e51d4e4ba12d6821b5 100644
--- a/src/database/entities/channel.rs
+++ b/src/database/entities/channel.rs
@@ -1,7 +1,7 @@
 use crate::database::*;
 use crate::notifications::events::ClientboundNotification;
 use crate::util::result::{Error, Result};
-use mongodb::bson::{doc, to_document};
+use mongodb::bson::{doc, from_document, to_document};
 use rocket_contrib::json::JsonValue;
 use serde::{Deserialize, Serialize};
 
@@ -52,6 +52,20 @@ impl Channel {
         }
     }
 
+    pub async fn get(id: &str) -> Result<Channel> {
+        let doc = get_collection("channels")
+            .find_one(
+                doc! { "_id": id },
+                None
+            )
+            .await
+            .map_err(|_| Error::DatabaseError { operation: "find_one", with: "channel" })?
+            .ok_or_else(|| Error::UnknownChannel)?;
+        
+        from_document::<Channel>(doc)
+            .map_err(|_| Error::DatabaseError { operation: "from_document", with: "channel" })
+    }
+
     pub async fn publish(self) -> Result<()> {
         get_collection("channels")
             .insert_one(
diff --git a/src/database/entities/message.rs b/src/database/entities/message.rs
index 8f8fbe75dfa5c939ccb6de4d62bf3d9dc7de07b5..8594f40052d37b8d1742dc1ea3a99240be18a09d 100644
--- a/src/database/entities/message.rs
+++ b/src/database/entities/message.rs
@@ -44,7 +44,7 @@ impl Message {
                 with: "message",
             })?;
 
-        // ! temp code
+        // ! FIXME: temp code
         let channels = get_collection("channels");
         match channel {
             Channel::DirectMessage { id, .. } => {
diff --git a/src/database/permissions/channel.rs b/src/database/permissions/channel.rs
index 7c216b0fb1fa22b84a3dad9d8994734c34cc2bde..e1df309d8c4f837ada3da33abcf081897806a6a3 100644
--- a/src/database/permissions/channel.rs
+++ b/src/database/permissions/channel.rs
@@ -1,4 +1,8 @@
 use crate::database::*;
+use crate::util::result::Result;
+
+use super::PermissionCalculator;
+
 use num_enum::TryFromPrimitive;
 use std::ops;
 
@@ -21,40 +25,90 @@ bitfield! {
 impl_op_ex!(+ |a: &ChannelPermission, b: &ChannelPermission| -> u32 { *a as u32 | *b as u32 });
 impl_op_ex_commutative!(+ |a: &u32, b: &ChannelPermission| -> u32 { *a | *b as u32 });
 
-pub async fn calculate(user: &User, target: &Channel) -> ChannelPermissions<[u32; 1]> {
+/*pub async fn calculate(user: &User, target: &Channel) -> Result<u32> {
     match target {
         Channel::SavedMessages { user: owner, .. } => {
             if &user.id == owner {
-                ChannelPermissions([ChannelPermission::View
+                Ok(ChannelPermission::View
                     + ChannelPermission::SendMessage
-                    + ChannelPermission::ManageMessages])
+                    + ChannelPermission::ManageMessages)
             } else {
-                ChannelPermissions([0])
+                Ok(0)
             }
         }
         Channel::DirectMessage { recipients, .. } => {
             if recipients.iter().find(|x| *x == &user.id).is_some() {
                 if let Some(recipient) = recipients.iter().find(|x| *x != &user.id) {
-                    let perms = super::user::calculate(&user, recipient).await;
+                    let perms = super::user::get(&user, recipient).await?;
 
                     if perms.get_send_message() {
-                        return ChannelPermissions([
-                            ChannelPermission::View + ChannelPermission::SendMessage
-                        ]);
+                        return Ok(ChannelPermission::View + ChannelPermission::SendMessage);
                     }
 
-                    return ChannelPermissions([ChannelPermission::View as u32]);
+                    return Ok(ChannelPermission::View as u32);
                 }
             }
 
-            ChannelPermissions([0])
+            Ok(0)
         }
         Channel::Group { recipients, .. } => {
             if recipients.iter().find(|x| *x == &user.id).is_some() {
-                ChannelPermissions([ChannelPermission::View + ChannelPermission::SendMessage])
+                Ok(ChannelPermission::View + ChannelPermission::SendMessage)
             } else {
-                ChannelPermissions([0])
+                Ok(0)
             }
         }
     }
 }
+
+pub async fn get(user: &User, target: &Channel) -> Result<ChannelPermissions<[u32; 1]>> {
+    Ok(ChannelPermissions([calculate(&user, &target).await?]))
+}*/
+
+impl<'a> PermissionCalculator<'a> {
+    pub async fn calculate_channel(self) -> Result<u32> {
+        let channel = if let Some(channel) = self.channel {
+            channel
+        } else {
+            unreachable!()
+        };
+
+        match channel {
+            Channel::SavedMessages { user: owner, .. } => {
+                if &self.perspective.id == owner {
+                    Ok(ChannelPermission::View
+                        + ChannelPermission::SendMessage
+                        + ChannelPermission::ManageMessages)
+                } else {
+                    Ok(0)
+                }
+            }
+            Channel::DirectMessage { recipients, .. } => {
+                if recipients.iter().find(|x| *x == &self.perspective.id).is_some() {
+                    if let Some(recipient) = recipients.iter().find(|x| *x != &self.perspective.id) {
+                        let perms = self.for_user(recipient).await?;
+    
+                        if perms.get_send_message() {
+                            return Ok(ChannelPermission::View + ChannelPermission::SendMessage);
+                        }
+    
+                        return Ok(ChannelPermission::View as u32);
+                    }
+                }
+    
+                Ok(0)
+            }
+            Channel::Group { recipients, .. } => {
+                if recipients.iter().find(|x| *x == &self.perspective.id).is_some() {
+                    Ok(ChannelPermission::View + ChannelPermission::SendMessage)
+                } else {
+                    Ok(0)
+                }
+            }
+        }
+    }
+
+    pub async fn for_channel(self) -> Result<ChannelPermissions<[u32; 1]>> {
+        Ok(ChannelPermissions([ self.calculate_channel().await? ]))
+    }
+}
diff --git a/src/database/permissions/mod.rs b/src/database/permissions/mod.rs
index 0aea9198e39f5bae4b519d96801b938badd37eba..86344e47f4f94a0c9cd6bdc20fde92b77219cc73 100644
--- a/src/database/permissions/mod.rs
+++ b/src/database/permissions/mod.rs
@@ -1,4 +1,38 @@
+pub use crate::database::*;
+
 pub mod channel;
 pub mod user;
 
 pub use user::get_relationship;
+
+pub struct PermissionCalculator<'a> {
+    perspective: &'a User,
+
+    user: Option<&'a User>,
+    channel: Option<&'a Channel>,
+}
+
+impl<'a> PermissionCalculator<'a> {
+    pub fn new(perspective: &'a User) -> PermissionCalculator {
+        PermissionCalculator {
+            perspective,
+
+            user: None,
+            channel: None
+        }
+    }
+
+    pub fn with_user(self, user: &'a User) -> PermissionCalculator {
+        PermissionCalculator {
+            user: Some(&user),
+            ..self
+        }
+    }
+
+    pub fn with_channel(self, channel: &'a Channel) -> PermissionCalculator {
+        PermissionCalculator {
+            channel: Some(&channel),
+            ..self
+        }
+    }
+}
diff --git a/src/database/permissions/user.rs b/src/database/permissions/user.rs
index 951afe5598ac0fe41afbc30512928030d4f5f224..2b58c3be558f50892c2bc62ab7f808f63535822a 100644
--- a/src/database/permissions/user.rs
+++ b/src/database/permissions/user.rs
@@ -1,52 +1,33 @@
 use crate::database::*;
-use num_enum::TryFromPrimitive;
+use crate::util::result::{Error, Result};
+
+use super::PermissionCalculator;
+
 use std::ops;
+use mongodb::bson::doc;
+use num_enum::TryFromPrimitive;
 
 #[derive(Debug, PartialEq, Eq, TryFromPrimitive, Copy, Clone)]
 #[repr(u32)]
 pub enum UserPermission {
     Access = 1,
-    SendMessage = 2,
-    Invite = 4,
+    ViewProfile = 2,
+    SendMessage = 4,
+    Invite = 8
 }
 
 bitfield! {
     pub struct UserPermissions(MSB0 [u32]);
     u32;
     pub get_access, _: 31;
-    pub get_send_message, _: 30;
-    pub get_invite, _: 29;
+    pub get_view_profile, _: 30;
+    pub get_send_message, _: 29;
+    pub get_invite, _: 28;
 }
 
 impl_op_ex!(+ |a: &UserPermission, b: &UserPermission| -> u32 { *a as u32 | *b as u32 });
 impl_op_ex_commutative!(+ |a: &u32, b: &UserPermission| -> u32 { *a | *b as u32 });
 
-pub async fn calculate(user: &User, target: &str) -> UserPermissions<[u32; 1]> {
-    // if friends; Access + Message + Invite
-    // if mutually know each other:
-    //    and has DMs from users enabled -> Access + Message
-    //    otherwise -> Access
-    // otherwise; None
-
-    let mut permissions: u32 = 0;
-    match get_relationship(&user, &target) {
-        RelationshipStatus::Friend => {
-            return UserPermissions([UserPermission::Access
-                + UserPermission::SendMessage
-                + UserPermission::Invite])
-        }
-        RelationshipStatus::Blocked | RelationshipStatus::BlockedOther => {
-            return UserPermissions([UserPermission::Access as u32])
-        }
-        RelationshipStatus::Incoming | RelationshipStatus::Outgoing => {
-            permissions = UserPermission::Access as u32;
-        }
-        _ => {}
-    }
-
-    UserPermissions([permissions])
-}
-
 pub fn get_relationship(a: &User, b: &str) -> RelationshipStatus {
     if a.id == b {
         return RelationshipStatus::Friend;
@@ -60,3 +41,53 @@ pub fn get_relationship(a: &User, b: &str) -> RelationshipStatus {
 
     RelationshipStatus::None
 }
+
+impl<'a> PermissionCalculator<'a> {
+    pub async fn calculate_user(self, target: &str) -> Result<u32> {
+        let mut permissions: u32 = 0;
+        match get_relationship(&self.perspective, &target) {
+            RelationshipStatus::Friend => {
+                return Ok(u32::MAX)
+            }
+            RelationshipStatus::Blocked | RelationshipStatus::BlockedOther => {
+                return Ok(UserPermission::Access as u32)
+            }
+            RelationshipStatus::Incoming | RelationshipStatus::Outgoing => {
+                permissions = UserPermission::Access as u32;
+                // ! INFO: if we add boolean switch for permission to
+                // ! message people who have mutual, we need to get
+                // ! rid of this return statement.
+                return Ok(permissions);
+            }
+            _ => {}
+        }
+
+        if get_collection("channels")
+            .find_one(
+                doc! {
+                    "type": "Group",
+                    "$and": {
+                        "recipients": &self.perspective.id,
+                        "recipients": target
+                    }
+                },
+                None
+            )
+            .await
+            .map_err(|_| Error::DatabaseError { operation: "find", with: "channels" })?
+            .is_some() {
+            return Ok(UserPermission::Access as u32);
+        }
+
+        Ok(permissions)
+    }
+
+    pub async fn for_user(self, target: &str) -> Result<UserPermissions<[u32; 1]>> {
+        Ok(UserPermissions([ self.calculate_user(&target).await? ]))
+    }
+
+    pub async fn for_user_given(self) -> Result<UserPermissions<[u32; 1]>> {
+        let id = &self.user.unwrap().id;
+        Ok(UserPermissions([ self.calculate_user(&id).await? ]))
+    }
+}
diff --git a/src/routes/channels/delete_channel.rs b/src/routes/channels/delete_channel.rs
index 0b8e30fd91a0e800ded9859aa66de93d5dd5ec28..7e01b5dc61a2c785446bbe62cb46e9e64a479451 100644
--- a/src/routes/channels/delete_channel.rs
+++ b/src/routes/channels/delete_channel.rs
@@ -7,7 +7,9 @@ use mongodb::bson::doc;
 pub async fn req(user: User, target: Ref) -> Result<()> {
     let target = target.fetch_channel().await?;
 
-    let perm = permissions::channel::calculate(&user, &target).await;
+    let perm = permissions::PermissionCalculator::new(&user)
+        .with_channel(&target)
+        .for_channel().await?;
     if !perm.get_view() {
         Err(Error::LabelMe)?
     }
diff --git a/src/routes/channels/fetch_channel.rs b/src/routes/channels/fetch_channel.rs
index 4d9596018998d9dd2b0f9f2d772f6b382f6bd1cb..8016cfad964a36ccf82b68a2f108dbc472fcc27d 100644
--- a/src/routes/channels/fetch_channel.rs
+++ b/src/routes/channels/fetch_channel.rs
@@ -7,7 +7,9 @@ use rocket_contrib::json::JsonValue;
 pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
     let target = target.fetch_channel().await?;
 
-    let perm = permissions::channel::calculate(&user, &target).await;
+    let perm = permissions::PermissionCalculator::new(&user)
+        .with_channel(&target)
+        .for_channel().await?;
     if !perm.get_view() {
         Err(Error::LabelMe)?
     }
diff --git a/src/routes/channels/group_add_member.rs b/src/routes/channels/group_add_member.rs
index cbb97564a4294d0838bd7b814b46d62cd4fc74e0..0373a5a59b9556aa2b88dd95cb5fe7c063ba0605 100644
--- a/src/routes/channels/group_add_member.rs
+++ b/src/routes/channels/group_add_member.rs
@@ -11,8 +11,9 @@ pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> {
     }
 
     let channel = target.fetch_channel().await?;
-
-    let perm = permissions::channel::calculate(&user, &channel).await;
+    let perm = permissions::PermissionCalculator::new(&user)
+        .with_channel(&channel)
+        .for_channel().await?;
     if !perm.get_view() {
         Err(Error::LabelMe)?
     }
diff --git a/src/routes/channels/message_delete.rs b/src/routes/channels/message_delete.rs
index 67b88dc82da8cd4d5dbaa780f2c386b6abf04606..75875849d1a553bc80f0cd47868639050f2055ec 100644
--- a/src/routes/channels/message_delete.rs
+++ b/src/routes/channels/message_delete.rs
@@ -7,7 +7,9 @@ use mongodb::bson::doc;
 pub async fn req(user: User, target: Ref, msg: Ref) -> Result<()> {
     let channel = target.fetch_channel().await?;
 
-    let perm = permissions::channel::calculate(&user, &channel).await;
+    let perm = permissions::PermissionCalculator::new(&user)
+        .with_channel(&channel)
+        .for_channel().await?;
     if !perm.get_view() {
         Err(Error::LabelMe)?
     }
diff --git a/src/routes/channels/message_edit.rs b/src/routes/channels/message_edit.rs
index 5f170cd5d471f262f7b0987b6a1e72d3580d0633..e714d4122afb02a3fb5b4da6f6426b442bed3658 100644
--- a/src/routes/channels/message_edit.rs
+++ b/src/routes/channels/message_edit.rs
@@ -19,8 +19,9 @@ pub async fn req(user: User, target: Ref, msg: Ref, edit: Json<Data>) -> Result<
         .map_err(|error| Error::FailedValidation { error })?;
 
     let channel = target.fetch_channel().await?;
-
-    let perm = permissions::channel::calculate(&user, &channel).await;
+    let perm = permissions::PermissionCalculator::new(&user)
+        .with_channel(&channel)
+        .for_channel().await?;
     if !perm.get_view() {
         Err(Error::LabelMe)?
     }
diff --git a/src/routes/channels/message_fetch.rs b/src/routes/channels/message_fetch.rs
index 7b4ff9337b0fb1b44da143216244c07c0254270a..f497747c9077ca2a5735e505fd22adef09a81230 100644
--- a/src/routes/channels/message_fetch.rs
+++ b/src/routes/channels/message_fetch.rs
@@ -7,7 +7,9 @@ use rocket_contrib::json::JsonValue;
 pub async fn req(user: User, target: Ref, msg: Ref) -> Result<JsonValue> {
     let channel = target.fetch_channel().await?;
 
-    let perm = permissions::channel::calculate(&user, &channel).await;
+    let perm = permissions::PermissionCalculator::new(&user)
+        .with_channel(&channel)
+        .for_channel().await?;
     if !perm.get_view() {
         Err(Error::LabelMe)?
     }
diff --git a/src/routes/channels/message_query.rs b/src/routes/channels/message_query.rs
index 0bb128489000c8241f0113b652f4ea60736908d6..cff9529c5337fd25747cebe80658d524611f5bfb 100644
--- a/src/routes/channels/message_query.rs
+++ b/src/routes/channels/message_query.rs
@@ -29,7 +29,9 @@ pub async fn req(user: User, target: Ref, options: Form<Options>) -> Result<Json
 
     let target = target.fetch_channel().await?;
 
-    let perm = permissions::channel::calculate(&user, &target).await;
+    let perm = permissions::PermissionCalculator::new(&user)
+        .with_channel(&target)
+        .for_channel().await?;
     if !perm.get_view() {
         Err(Error::LabelMe)?
     }
diff --git a/src/routes/channels/message_send.rs b/src/routes/channels/message_send.rs
index 71a0f938864790f53fe7a4de99ea7d5d1b605166..986b4908f1a80d96b8f415175f213e0640116647 100644
--- a/src/routes/channels/message_send.rs
+++ b/src/routes/channels/message_send.rs
@@ -24,7 +24,9 @@ pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonVal
 
     let target = target.fetch_channel().await?;
 
-    let perm = permissions::channel::calculate(&user, &target).await;
+    let perm = permissions::PermissionCalculator::new(&user)
+        .with_channel(&target)
+        .for_channel().await?;
     if !perm.get_send_message() {
         Err(Error::LabelMe)?
     }
diff --git a/src/routes/users/fetch_user.rs b/src/routes/users/fetch_user.rs
index 8c4b91c6a88fc7909b575d49af7c6d648cbbd967..f9b7a1bf416c4b3b60cda20c0ae2761ee1d11a56 100644
--- a/src/routes/users/fetch_user.rs
+++ b/src/routes/users/fetch_user.rs
@@ -9,7 +9,9 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
 
     if user.id != target.id {
         // Check whether we are allowed to fetch this user.
-        let perm = permissions::user::calculate(&user, &target.id).await;
+        let perm = permissions::PermissionCalculator::new(&user)
+            .with_user(&target)
+            .for_user_given().await?;
         if !perm.get_access() {
             Err(Error::LabelMe)?
         }
diff --git a/src/util/result.rs b/src/util/result.rs
index 7aa3e033b05772c75f532ab0ad260fa500751be1..4a548032b98e18d88791d1c62c2053b36754cdc6 100644
--- a/src/util/result.rs
+++ b/src/util/result.rs
@@ -34,6 +34,8 @@ pub enum Error {
     NotFriends,
 
     // ? Channel related errors.
+    #[snafu(display("This channel does not exist!"))]
+    UnknownChannel,
     #[snafu(display("Cannot edit someone else's message."))]
     CannotEditMessage,
     #[snafu(display("Cannot remove yourself from a group, use delete channel instead."))]
@@ -81,6 +83,7 @@ impl<'r> Responder<'r, 'static> for Error {
             Error::BlockedByOther => Status::Forbidden,
             Error::NotFriends => Status::Forbidden,
 
+            Error::UnknownChannel => Status::NotFound,
             Error::CannotEditMessage => Status::Forbidden,
             Error::CannotRemoveYourself => Status::BadRequest,
             Error::GroupTooLarge { .. } => Status::Forbidden,