| 
						
						
							
								
							
						
						
					 | 
					@ -20,10 +20,12 @@ static SHOW_WEBSOCKETS_MSG: AtomicBool = AtomicBool::new(true); | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					#[get("/hub")] | 
					 | 
					 | 
					#[get("/hub")] | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					fn websockets_err() -> EmptyResult { | 
					 | 
					 | 
					fn websockets_err() -> EmptyResult { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					    if CONFIG.websocket_enabled() && SHOW_WEBSOCKETS_MSG.compare_and_swap(true, false, Ordering::Relaxed) { | 
					 | 
					 | 
					    if CONFIG.websocket_enabled() && SHOW_WEBSOCKETS_MSG.compare_and_swap(true, false, Ordering::Relaxed) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					        err!("########################################################### | 
					 | 
					 | 
					        err!( | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					    "########################################################### | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					    '/notifications/hub' should be proxied to the websocket server or notifications won't work. | 
					 | 
					 | 
					    '/notifications/hub' should be proxied to the websocket server or notifications won't work. | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					    Go to the Wiki for more info, or disable WebSockets setting WEBSOCKET_ENABLED=false. | 
					 | 
					 | 
					    Go to the Wiki for more info, or disable WebSockets setting WEBSOCKET_ENABLED=false. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					    ###########################################################################################") | 
					 | 
					 | 
					    ###########################################################################################" | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					        ) | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					    } else { | 
					 | 
					 | 
					    } else { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					        Err(Error::empty()) | 
					 | 
					 | 
					        Err(Error::empty()) | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					    } | 
					 | 
					 | 
					    } | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					@ -137,7 +139,6 @@ struct InitialMessage { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					const PING_MS: u64 = 15_000; | 
					 | 
					 | 
					const PING_MS: u64 = 15_000; | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					const PING: Token = Token(1); | 
					 | 
					 | 
					const PING: Token = Token(1); | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					
 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					const ID_KEY: &str = "id="; | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					const ACCESS_TOKEN_KEY: &str = "access_token="; | 
					 | 
					 | 
					const ACCESS_TOKEN_KEY: &str = "access_token="; | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					
 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					impl WSHandler { | 
					 | 
					 | 
					impl WSHandler { | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					@ -148,6 +149,32 @@ impl WSHandler { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					        let io_error = io::Error::from(io::ErrorKind::InvalidData); | 
					 | 
					 | 
					        let io_error = io::Error::from(io::ErrorKind::InvalidData); | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					        Err(ws::Error::new(ws::ErrorKind::Io(io_error), msg)) | 
					 | 
					 | 
					        Err(ws::Error::new(ws::ErrorKind::Io(io_error), msg)) | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					    } | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					    fn get_request_token(&self, hs: Handshake) -> Option<String> { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					        use std::str::from_utf8; | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					        // Verify we have a token header
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					        if let Some(header_value) = hs.request.header("Authorization") { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					            if let Ok(converted) = from_utf8(header_value) { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					                if let Some(token_part) = converted.split("Bearer ").nth(1) { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					                    return Some(token_part.into()); | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					                } | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					            } | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					        }; | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					        
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					        // Otherwise verify the query parameter value
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					        let path = hs.request.resource(); | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					        if let Some(params) = path.split('?').nth(1) { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					            let params_iter = params.split('&').take(1); | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					            for val in params_iter { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					                if val.starts_with(ACCESS_TOKEN_KEY) { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					                    return Some(val[ACCESS_TOKEN_KEY.len()..].into()); | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					                } | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					            } | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					        }; | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					        None | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					} | 
					 | 
					 | 
					} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					
 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					impl Handler for WSHandler { | 
					 | 
					 | 
					impl Handler for WSHandler { | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					@ -156,35 +183,16 @@ impl Handler for WSHandler { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					        //
 | 
					 | 
					 | 
					        //
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					        // We don't use `id`, and as of around 2020-03-25, the official clients
 | 
					 | 
					 | 
					        // We don't use `id`, and as of around 2020-03-25, the official clients
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					        // no longer seem to pass `id` (only `access_token`).
 | 
					 | 
					 | 
					        // no longer seem to pass `id` (only `access_token`).
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					        let path = hs.request.resource(); | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					
 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					        let (_id, access_token) = match path.split('?').nth(1) { | 
					 | 
					 | 
					        // Get user token from header or query parameter
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					            Some(params) => { | 
					 | 
					 | 
					        let access_token = match self.get_request_token(hs) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					                let params_iter = params.split('&').take(2); | 
					 | 
					 | 
					            Some(token) => token, | 
				
			
			
				
				
			
		
	
		
		
			
				
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					                let mut id = None; | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					                let mut access_token = None; | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					                for val in params_iter { | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					                    if val.starts_with(ID_KEY) { | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					                        id = Some(&val[ID_KEY.len()..]); | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					                    } else if val.starts_with(ACCESS_TOKEN_KEY) { | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					                        access_token = Some(&val[ACCESS_TOKEN_KEY.len()..]); | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					                    } | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					                } | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					                match (id, access_token) { | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					                    (Some(a), Some(b)) => (a, b), | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					                    (None, Some(b)) => ("", b), // Ignore missing `id`.
 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					 | 
					 | 
					            _ => return self.err("Missing access token"), | 
					 | 
					 | 
					            _ => return self.err("Missing access token"), | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					                } | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					            } | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					            None => return self.err("Missing query parameters"), | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					        }; | 
					 | 
					 | 
					        }; | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					
 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					        // Validate the user
 | 
					 | 
					 | 
					        // Validate the user
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					        use crate::auth; | 
					 | 
					 | 
					        use crate::auth; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					        let claims = match auth::decode_login(access_token) { | 
					 | 
					 | 
					        let claims = match auth::decode_login(access_token.as_str()) { | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					 | 
					 | 
					            Ok(claims) => claims, | 
					 | 
					 | 
					            Ok(claims) => claims, | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					            Err(_) => return self.err("Invalid access token provided"), | 
					 | 
					 | 
					            Err(_) => return self.err("Invalid access token provided"), | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					        }; | 
					 | 
					 | 
					        }; | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					@ -335,7 +343,7 @@ impl WebSocketUsers { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					/* Message Structure
 | 
					 | 
					 | 
					/* Message Structure
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					[ | 
					 | 
					 | 
					[ | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					    1, // MessageType.Invocation
 | 
					 | 
					 | 
					    1, // MessageType.Invocation
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					    {}, // Headers
 | 
					 | 
					 | 
					    {}, // Headers (map)
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					 | 
					 | 
					    null, // InvocationId
 | 
					 | 
					 | 
					    null, // InvocationId
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					    "ReceiveMessage", // Target
 | 
					 | 
					 | 
					    "ReceiveMessage", // Target
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					    [ // Arguments
 | 
					 | 
					 | 
					    [ // Arguments
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					@ -352,7 +360,7 @@ fn create_update(payload: Vec<(Value, Value)>, ut: UpdateType) -> Vec<u8> { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					
 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					    let value = V::Array(vec![ | 
					 | 
					 | 
					    let value = V::Array(vec![ | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					        1.into(), | 
					 | 
					 | 
					        1.into(), | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					        V::Array(vec![]), | 
					 | 
					 | 
					        V::Map(vec![]), | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					 | 
					 | 
					        V::Nil, | 
					 | 
					 | 
					        V::Nil, | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					        "ReceiveMessage".into(), | 
					 | 
					 | 
					        "ReceiveMessage".into(), | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					        V::Array(vec![V::Map(vec![ | 
					 | 
					 | 
					        V::Array(vec![V::Map(vec![ | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
					
  |