Skip to content

Commit

Permalink
Special chars support
Browse files Browse the repository at this point in the history
Using the percent_encoding crate, now we support all the utf8 chars
into host, pass, user, db path and fb lib path. To use some chars, like
the @ you must use the '%' equivalent. E.g: @ => %40.
  • Loading branch information
fernandobatels committed Dec 2, 2020
1 parent 5608eb5 commit 953f893
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 9 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ rsfbclient-native = { version = "0.12.0", path = "rsfbclient-native", optional =
rsfbclient-rust = { version = "0.12.0", path = "rsfbclient-rust", optional = true }
rsfbclient-derive = { version = "0.12.0", path = "rsfbclient-derive" }
url = "2.2.0"
percent-encoding = "2.1.0"

[dev-dependencies]
rand = "0.7.3"
Expand Down
3 changes: 1 addition & 2 deletions rsfbclient-core/src/charset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ impl Charset {
.into()
})
} else {
String::from_utf8(bytes.into_owned())
.map_err(|e| format!("Found column with an invalid UTF-8 string: {}", e).into())
String::from_utf8(bytes.into_owned()).map_err(|e| e.into())
}
}

Expand Down
14 changes: 14 additions & 0 deletions rsfbclient-core/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! Error type for the connection
use std::str::Utf8Error;
use std::string::FromUtf8Error;
use thiserror::Error;

use crate::SqlType;
Expand Down Expand Up @@ -28,6 +30,18 @@ impl From<&str> for FbError {
}
}

impl From<FromUtf8Error> for FbError {
fn from(e: FromUtf8Error) -> Self {
Self::Other(format!("Found column with an invalid UTF-8 string: {}", e))
}
}

impl From<Utf8Error> for FbError {
fn from(e: Utf8Error) -> Self {
Self::Other(format!("Found column with an invalid UTF-8 string: {}", e))
}
}

pub fn err_column_null(type_name: &str) -> FbError {
FbError::Other(format!(
"This is a null value. Use the Option<{}> to safe access this column and avoid errors",
Expand Down
1 change: 0 additions & 1 deletion rsfbclient-native/src/row.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ impl ColumnBuffer {
.collect::<Vec<u8>>();

String::from_utf8(bname)
.map_err(|_| FbError::from("Found a column name with an invalid utf-8 string"))
}?;

Ok(ColumnBuffer {
Expand Down
80 changes: 74 additions & 6 deletions src/connection/conn_string.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Connection string parser
use crate::*;
use percent_encoding::percent_decode_str;
use std::str::FromStr;
use url::Url;

Expand Down Expand Up @@ -31,22 +32,29 @@ pub fn parse(sconn: &str) -> Result<ConnStringSettings, FbError> {

let user = match url.username() {
"" => None,
u => Some(u.to_string()),
u => Some(percent_decode_str(u).decode_utf8()?.into_owned()),
};

let pass = url.password().map(|p| p.to_string());
let pass = match url.password() {
Some(p) => Some(percent_decode_str(p).decode_utf8()?.into_owned()),
_ => None,
};

let mut host = url.host().map(|h| h.to_string());
let mut host = match url.host_str() {
Some(h) => Some(percent_decode_str(h).decode_utf8()?.into_owned()),
_ => None,
};

let port = url.port();

let mut db_name = match url.path() {
"" => None,
db => {
let db = percent_decode_str(db).decode_utf8()?.into_owned();
if db.starts_with('/') && url.has_host() {
Some(db.replacen("/", "", 1))
} else {
Some(db.to_string())
Some(db)
}
}
};
Expand Down Expand Up @@ -94,7 +102,7 @@ pub fn parse(sconn: &str) -> Result<ConnStringSettings, FbError> {
for (param, val) in url.query_pairs() {
match param.to_string().as_str() {
"lib" => {
lib_path = Some(val.to_string());
lib_path = Some(percent_decode_str(&val).decode_utf8()?.into_owned());
}
"dialect" => {
dialect = match Dialect::from_str(&val) {
Expand Down Expand Up @@ -136,6 +144,65 @@ mod test {
use super::parse;
use crate::*;

#[test]
fn special_chars() -> Result<(), FbError> {
// User with an ã
let conn = parse("firebird://joão:maria@localhost:3050//srv/db/database_name.fdb")?;

assert_eq!(Some("joão".to_string()), conn.user);
assert_eq!(Some("maria".to_string()), conn.pass);
assert_eq!(Some("localhost".to_string()), conn.host);
assert_eq!(Some(3050), conn.port);
assert_eq!("/srv/db/database_name.fdb".to_string(), conn.db_name);

// User with @
let conn = parse("firebird://joão%402000:maria@localhost:3050//srv/db/database_name.fdb")?;

assert_eq!(Some("joão@2000".to_string()), conn.user);
assert_eq!(Some("maria".to_string()), conn.pass);
assert_eq!(Some("localhost".to_string()), conn.host);
assert_eq!(Some(3050), conn.port);
assert_eq!("/srv/db/database_name.fdb".to_string(), conn.db_name);

// Pass with special chars
let conn = parse("firebird://sysdba:abc%40_!@localhost:3050//srv/db/database_name.fdb")?;

assert_eq!(Some("sysdba".to_string()), conn.user);
assert_eq!(Some("abc@_!".to_string()), conn.pass);
assert_eq!(Some("localhost".to_string()), conn.host);
assert_eq!(Some(3050), conn.port);
assert_eq!("/srv/db/database_name.fdb".to_string(), conn.db_name);

// host with special chars
let conn = parse("firebird://sysdba:abc@joãohost.com.br:3050//srv/db/database_name.fdb")?;

assert_eq!(Some("sysdba".to_string()), conn.user);
assert_eq!(Some("abc".to_string()), conn.pass);
assert_eq!(Some("joãohost.com.br".to_string()), conn.host);
assert_eq!(Some(3050), conn.port);
assert_eq!("/srv/db/database_name.fdb".to_string(), conn.db_name);

// db path with special chars
let conn = parse("firebird:///joão/automação/db.fdb")?;

assert_eq!(None, conn.user);
assert_eq!(None, conn.pass);
assert_eq!(None, conn.host);
assert_eq!(None, conn.port);
assert_eq!("/joão/automação/db.fdb".to_string(), conn.db_name);

// db lib path with special chars
let conn = parse("firebird:///tmp/database_name.fdb?lib=/tmp/localização/fbclient.lib")?;

assert_eq!("/tmp/database_name.fdb".to_string(), conn.db_name);
assert_eq!(
Some("/tmp/localização/fbclient.lib".to_string()),
conn.lib_path
);

Ok(())
}

#[test]
fn params_combination() -> Result<(), FbError> {
let conn = parse("firebird:///srv/db/database_name.fdb?lib=/tmp/fbclient.lib&stmt_cache_size=1&dialect=1&charset=utf8")?;
Expand Down Expand Up @@ -309,7 +376,8 @@ mod test {
assert_eq!("database_name.fdb".to_string(), conn.db_name);

// only user provided, but with a blank ':' char
let conn = parse("firebird://username:@192.168.0.1:3050/c:/db/database_name.fdb?dialect=3")?;
let conn =
parse("firebird://username:@192.168.0.1:3050/c:/db/database_name.fdb?dialect=3")?;

assert_eq!(Some("username".to_string()), conn.user);
assert_eq!(None, conn.pass);
Expand Down

0 comments on commit 953f893

Please sign in to comment.