diesel -> sqlx, patch cve (again lul)

This commit is contained in:
Reid 2023-10-31 19:40:49 -07:00
parent a5cac129dd
commit c3bb6d0d67
Signed by: reidlab
GPG key ID: 6C9EAA3364F962C8
39 changed files with 1581 additions and 1207 deletions

View file

@ -1,3 +1,2 @@
pub mod login_account;
pub mod register_account;
pub mod update_account_settings;
pub mod register_account;

View file

@ -2,8 +2,6 @@ use rocket::form::Form;
use rocket::http::Status;
use rocket::response::status;
use diesel::prelude::*;
use crate::helpers;
use crate::db;
@ -17,46 +15,43 @@ pub struct FromLoginAccount {
}
#[post("/accounts/loginGJAccount.php", data = "<input>")]
pub fn login_account(input: Form<FromLoginAccount>) -> status::Custom<&'static str> {
let connection = &mut db::establish_connection_pg();
pub async fn login_account(input: Form<FromLoginAccount>) -> status::Custom<&'static str> {
let connection = &mut db::establish_sqlite_conn().await;
if input.userName != helpers::clean::clean_no_space(input.userName.as_ref()) {
let username = helpers::clean::clean_basic(input.userName.as_ref());
let password = input.password.clone();
let gjp = input.gjp.clone();
let gjp2 = input.gjp2.clone();
if input.userName != username {
return status::Custom(Status::Ok, "-4")
}
// gjp2 checks dont matter, its hashed, gjp checks would break bc its base64, and why does this check exist if its just for logging in robtop this is useless it doesnt provide security we already did the security on the register account u fucking faggot im really bored of working on this but im also excited to see if it works deepwoken solos mid dash
match input.password.clone() {
Some(password_val) => {
if password_val.len() < 6 {
return status::Custom(Status::Ok, "-8")
}
},
None => {}
// why does this check exist? it's kinda useless
if let Some(password) = password {
if password.len() < 6 {
return status::Custom(Status::Ok, "-8")
}
}
if input.userName.len() < 3 {
if username.len() < 3 {
return status::Custom(Status::Ok, "-9")
}
// account verification
{
use db::schema::accounts::dsl::*;
let result = sqlx::query_scalar!("SELECT id FROM accounts WHERE username = ?", username)
.fetch_one(connection)
.await;
let query_result = accounts
.select(id)
.filter(username.eq(input.userName.clone()))
.get_result::<i32, >(connection);
match result {
Ok(account_id_val) => {
let user_id_val = helpers::accounts::get_user_id_from_account_id(account_id_val).await;
match query_result {
Ok(account_id_val) => {
let user_id_val = helpers::accounts::get_user_id_from_account_id(account_id_val);
match helpers::accounts::auth(account_id_val, input.password.clone(), input.gjp.clone(), input.gjp2.clone()) {
Ok(_) => return status::Custom(Status::Ok, Box::leak(format!("{},{}", user_id_val, account_id_val).into_boxed_str())),
Err(_) => return status::Custom(Status::Ok, "-11")
}
},
Err(_) => return status::Custom(Status::Ok, "-1")
}
match helpers::accounts::auth(account_id_val, input.password.clone(), input.gjp.clone(), input.gjp2.clone()).await {
Ok(_) => return status::Custom(Status::Ok, Box::leak(format!("{},{}", user_id_val, account_id_val).into_boxed_str())),
Err(_) => return status::Custom(Status::Ok, "-11")
}
},
Err(_) => return status::Custom(Status::Ok, "-1")
}
}

View file

@ -2,9 +2,6 @@ use rocket::form::Form;
use rocket::http::Status;
use rocket::response::status;
use diesel::prelude::*;
use diesel::result::Error;
use password_auth::generate_hash;
use crate::config;
@ -19,77 +16,59 @@ pub struct FormRegisterAccount {
}
#[post("/accounts/registerGJAccount.php", data = "<input>")]
pub fn register_account(input: Form<FormRegisterAccount>) -> status::Custom<&'static str> {
let connection = &mut db::establish_connection_pg();
pub async fn register_account(input: Form<FormRegisterAccount>) -> status::Custom<&'static str> {
let mut connection = db::establish_sqlite_conn().await;
let username = helpers::clean::clean_basic(input.userName.as_ref());
let password = input.password.clone();
let email = input.email.clone();
let hashed_password = generate_hash(password.clone());
let gjp2 = helpers::encryption::get_gjp2(password.clone());
if config::config_get_with_default("accounts.allow_registration", true) == false {
return status::Custom(Status::Ok, "-1")
}
if input.userName != helpers::clean::clean_no_space(input.userName.as_ref()) {
if input.userName != username {
return status::Custom(Status::Ok, "-4")
}
if input.password.len() < 6 {
if password.len() < 6 {
return status::Custom(Status::Ok, "-8")
}
if input.userName.len() < 3 {
if username.len() < 3 {
return status::Custom(Status::Ok, "-9")
}
if input.userName.len() > 20 {
if username.len() > 20 {
return status::Custom(Status::Ok, "-4")
}
if input.email.len() > 254 {
if email.len() > 254 {
return status::Custom(Status::Ok, "-6")
}
// account management
use db::models::{Account, NewAccount};
// check if the username is already taken
sqlx::query_scalar!("SELECT COUNT(*) FROM accounts WHERE username = ?", username)
.fetch_one(&mut connection)
.await
.map_err(|_| status::Custom(Status::Ok, "-1"))
.expect("error getting the account count");
let inserted_account: Account;
let inserted_account = sqlx::query!("INSERT INTO accounts (username, password, email, gjp2) VALUES (?, ?, ?, ?)", username, hashed_password, email, gjp2)
.execute(&mut connection)
.await
.expect("error saving the new account");
{
use db::schema::accounts::dsl::*;
let account_name_usage = accounts.filter(username.eq(input.userName.clone())).count().get_result::<i64>(connection) as Result<i64, Error>;
let account_name_used = account_name_usage.expect("database name query error") != 0;
if account_name_used {
return status::Custom(Status::Ok, "-2")
}
let new_account = NewAccount {
username: input.userName.clone(),
password: generate_hash(input.password.clone()),
gjp2: generate_hash(helpers::encryption::get_gjp2(input.password.clone())),
email: input.email.clone()
};
inserted_account = diesel::insert_into(accounts)
.values(&new_account)
.get_result::<Account, >(connection)
.expect("error saving the new account");
}
let inserted_account_id = inserted_account.last_insert_rowid();
// user management
{
use db::models::{User, NewUser};
use db::schema::users::dsl::*;
sqlx::query!("INSERT INTO users (account_id, username, registered) VALUES (?, ?, 1)", inserted_account_id, username)
.execute(&mut connection)
.await
.expect("error saving the new user");
let new_user = NewUser {
account_id: inserted_account.id,
username: input.userName.clone(),
registered: 1
};
diesel::insert_into(users)
.values(&new_user)
.get_result::<User, >(connection)
.expect("error saving the new user");
}
return status::Custom(Status::Ok, "1")
return status::Custom(Status::Ok, "1");
}

View file

@ -1,62 +0,0 @@
use rocket::form::Form;
use rocket::http::Status;
use rocket::response::status;
use diesel::prelude::*;
use crate::helpers;
use crate::db;
#[derive(FromForm)]
pub struct FormUpdateAccountSettings {
accountID: i32,
mS: i32,
frS: i32,
cS: i32,
yt: String,
twitter: String,
twitch: String,
password: Option<String>,
gjp: Option<String>,
gjp2: Option<String>,
}
#[post("/updateGJAccSettings20.php", data = "<input>")]
pub fn update_account_settings(input: Form<FormUpdateAccountSettings>) -> status::Custom<&'static str> {
let connection = &mut db::establish_connection_pg();
// account verification
let (_user_id_val, account_id_val): (i32, i32);
match helpers::accounts::auth(input.accountID.clone(), input.password.clone(), input.gjp.clone(), input.gjp2.clone()) {
Ok((user_id, account_id)) => {
_user_id_val = user_id;
account_id_val = account_id;
},
Err(_) => return status::Custom(Status::Ok, "-1")
};
{
use db::models::Account;
use db::schema::accounts::dsl::*;
diesel::update(accounts)
.filter(id.eq(account_id_val))
.set((
messages_enabled.eq(input.mS.clamp(0, 2)),
friend_requests_enabled.eq(input.frS.clamp(0, 1)),
comments_enabled.eq(input.cS.clamp(0, 2)),
youtube_url.eq(input.yt.chars().take(20).collect::<String>()),
twitch_url.eq(input.twitch.chars().take(25).collect::<String>()),
twitter_url.eq(input.twitter.chars().take(15).collect::<String>())
))
.get_result::<Account, >(connection)
.expect("failed to update account");
}
return status::Custom(Status::Ok, "1")
}

View file

@ -2,8 +2,6 @@ use rocket::form::Form;
use rocket::http::Status;
use rocket::response::status;
use diesel::prelude::*;
use base64::{Engine as _, engine::general_purpose};
use flate2::read::{GzDecoder, ZlibDecoder};
@ -24,16 +22,11 @@ pub struct FormDownloadLevel {
}
#[post("/downloadGJLevel22.php", data = "<input>")]
pub fn download_level(input: Form<FormDownloadLevel>) -> status::Custom<&'static str> {
let connection = &mut db::establish_connection_pg();
use db::schema::{levels, users};
use db::models::{Level, User};
pub async fn download_level(input: Form<FormDownloadLevel>) -> status::Custom<&'static str> {
let mut connection = db::establish_sqlite_conn().await;
let mut response: Vec<String> = Vec::new();
let query = levels::table.into_boxed();
match input.levelID {
-1 => {
unimplemented!("no daily support")
@ -49,168 +42,162 @@ pub fn download_level(input: Form<FormDownloadLevel>) -> status::Custom<&'static
}
}
// database query
{
let result = query
.filter(levels::id.eq(input.levelID))
.get_result::<Level, >(connection)
.expect("fatal error loading levels");
let result = sqlx::query!("SELECT levels.id, levels.name, levels.extra_data, levels.level_info, levels.password, levels.user_id, levels.description, levels.original, levels.game_version, levels.requested_stars, levels.version, levels.song_id, levels.length, levels.objects, levels.coins, levels.has_ldm, levels.two_player, levels.downloads, levels.likes, levels.difficulty, levels.community_difficulty, levels.demon_difficulty, levels.stars, levels.featured, levels.epic, levels.rated_coins, levels.created_at, levels.modified_at, users.username, users.udid, users.account_id, users.registered, editor_time, editor_time_copies FROM levels JOIN users ON levels.user_id = users.id WHERE levels.id = ?", input.levelID)
.fetch_one(&mut connection)
.await
.expect("error loading levels");
let user: User = users::table.find(result.user_id).get_result::<User, >(connection).expect("couldnt get user from lvl");
let level: Level = result;
let set_difficulty = match level.difficulty {
Some(diff) => {
Some(helpers::difficulty::LevelDifficulty::new(diff))
},
None => None
};
let community_difficulty = match level.community_difficulty {
Some(diff) => {
Some(helpers::difficulty::LevelDifficulty::new(diff))
},
None => None
};
let difficulty = match set_difficulty {
Some(diff) => {
Some(diff)
},
None => {
match community_difficulty {
Some(diff) => {
Some(diff)
},
None => None
}
let set_difficulty = match result.difficulty {
Some(diff) => {
Some(helpers::difficulty::LevelDifficulty::new(diff))
},
None => None
};
let community_difficulty = match result.community_difficulty {
Some(diff) => {
Some(helpers::difficulty::LevelDifficulty::new(diff))
},
None => None
};
let difficulty = match set_difficulty {
Some(diff) => {
Some(diff)
},
None => {
match community_difficulty {
Some(diff) => {
Some(diff)
},
None => None
}
};
let demon_difficulty = match level.demon_difficulty {
Some(diff) => {
Some(helpers::difficulty::DemonDifficulty::new(diff))
},
None => None
};
let xor_pass: String;
if input.gameVersion.unwrap_or(19) >= 20 {
xor_pass = general_purpose::URL_SAFE.encode(helpers::encryption::cyclic_xor_string(&level.password.clone().unwrap_or(String::from("0")), "26364"))
} else {
xor_pass = level.password.clone().unwrap_or(String::from("0"));
}
};
let demon_difficulty = match result.demon_difficulty {
Some(diff) => {
Some(helpers::difficulty::DemonDifficulty::new(diff))
},
None => None
};
let compressed_level_data = fs::read(format!("{}/{}/{}.lvl", config::config_get_with_default("db.data_folder", "data"), "levels", level.id)).expect("couldnt read level file");
let uncompressed_level_data = String::from_utf8(if compressed_level_data.starts_with(&[0x1F, 0x8B]) {
// gzip!!
let mut gz_decoder = GzDecoder::new(compressed_level_data.as_slice());
let mut decompressed_data = Vec::new();
gz_decoder.read_to_end(&mut decompressed_data).expect("err uncompressing level");
decompressed_data
} else if compressed_level_data.starts_with(&[0x78]) {
// zlib!!
let mut zlib_decoder = ZlibDecoder::new(compressed_level_data.as_slice());
let mut decompressed_data = Vec::new();
zlib_decoder.read_to_end(&mut decompressed_data).expect("err uncompressing level");
decompressed_data
} else {
panic!("invalid compression method")
}).expect("invalid utf-8 sequence");
let level_data = uncompressed_level_data.as_bytes();
response.push(helpers::format::format(hashmap! {
1 => level.id.to_string(),
2 => level.name,
3 => if input.gameVersion.unwrap_or(19) >= 20 {
general_purpose::URL_SAFE.encode(level.description)
} else {
level.description
},
4 => String::from_utf8(level_data.to_vec()).expect("invalid utf-8 sequence"),
5 => level.version.to_string(),
6 => user.id.to_string(),
// this argument is weird. its the "difficulty divisor"
// used to be vote count but yeah
8 => 10.to_string(),
9 => (match difficulty {
Some(diff) => diff.to_star_difficulty(),
None => 0
} * 10).to_string(),
10 => level.downloads.to_string(),
12 => (if level.song_id < 50 { level.song_id } else { 0 }).to_string(),
13 => level.game_version.to_string(),
14 => level.likes.to_string(),
16 => (-level.likes).to_string(),
15 => level.length.to_string(),
17 => match difficulty {
Some(diff) => {
if diff == helpers::difficulty::LevelDifficulty::Demon {
1
} else {
0
}
},
None => 0
}.to_string(),
18 => (if let Some(stars) = level.stars { stars } else { 0 }).to_string(),
19 => level.featured.to_string(),
25 => match difficulty {
Some(diff) => {
if diff == helpers::difficulty::LevelDifficulty::Auto {
1
} else {
0
}
},
None => 0
}.to_string(),
27 => xor_pass,
28 => "1".to_string(), // unimplemented
29 => "1".to_string(), // unimplemented
30 => (if let Some(original) = level.original { original } else { 0 }).to_string(),
31 => level.two_player.to_string(),
35 => (if level.song_id >= 50 { level.song_id } else { 0 }).to_string(),
36 => String::from_utf8(if input.extras.is_some() { level.extra_data } else { Vec::new() }).expect("invalid utf-8 sequence"),
37 => level.coins.to_string(),
38 => level.rated_coins.to_string(),
39 => (if let Some(requested_stars) = level.requested_stars { requested_stars } else { 0 }).to_string(),
40 => level.has_ldm.to_string(),
41 => "".to_string(), // unimplemented
42 => level.epic.to_string(),
43 => match demon_difficulty {
Some(diff) => {
diff
},
None => helpers::difficulty::DemonDifficulty::Hard
}.to_demon_difficulty().to_string(),
44 => "0".to_string(), // unimplemented
45 => level.objects.to_string(),
46 => level.editor_time.to_string(),
47 => level.editor_time_copies.to_string()
}));
response.push(helpers::encryption::gen_solo(String::from_utf8(level_data.to_vec()).expect("invalid utf-8 sequence")));
let thing = [
user.id.to_string(),
level.stars.unwrap_or(0).to_string(),
match difficulty {
Some(diff) => {
if diff == helpers::difficulty::LevelDifficulty::Demon {
1
} else {
0
}
},
None => 0
}.to_string(),
level.id.to_string(),
level.rated_coins.to_string(),
level.featured.to_string(),
level.password.unwrap_or(String::new()).to_string(),
0.to_string()
];
response.push(helpers::encryption::gen_solo_2(thing.join(",")));
let xor_pass: String;
if input.gameVersion.unwrap_or(19) >= 20 {
xor_pass = general_purpose::URL_SAFE.encode(helpers::encryption::cyclic_xor_string(&result.password.clone().unwrap_or(String::from("0")), "26364"))
} else {
xor_pass = result.password.clone().unwrap_or(String::from("0"));
}
let compressed_level_data = fs::read(format!("{}/{}/{}.lvl", config::config_get_with_default("db.data_folder", "data".to_string()), "levels", result.id)).expect("couldnt read level file");
let uncompressed_level_data = String::from_utf8(if compressed_level_data.starts_with(&[0x1F, 0x8B]) {
// gzip!!
let mut gz_decoder = GzDecoder::new(compressed_level_data.as_slice());
let mut decompressed_data = Vec::new();
gz_decoder.read_to_end(&mut decompressed_data).expect("err uncompressing level");
decompressed_data
} else if compressed_level_data.starts_with(&[0x78]) {
// zlib!!
let mut zlib_decoder = ZlibDecoder::new(compressed_level_data.as_slice());
let mut decompressed_data = Vec::new();
zlib_decoder.read_to_end(&mut decompressed_data).expect("err uncompressing level");
decompressed_data
} else {
panic!("invalid compression method")
}).expect("invalid utf-8 sequence");
let level_data = uncompressed_level_data.as_bytes();
response.push(helpers::format::format(hashmap! {
1 => result.id.to_string(),
2 => result.name,
3 => if input.gameVersion.unwrap_or(19) >= 20 {
general_purpose::URL_SAFE.encode(result.description)
} else {
result.description
},
4 => String::from_utf8(level_data.to_vec()).expect("invalid utf-8 sequence"),
5 => result.version.to_string(),
6 => result.user_id.to_string(),
// this argument is weird. its the "difficulty divisor"
// used to be vote count but yeah
8 => 10.to_string(),
9 => (match difficulty {
Some(diff) => diff.to_star_difficulty(),
None => 0
} * 10).to_string(),
10 => result.downloads.to_string(),
12 => (if result.song_id < 50 { result.song_id } else { 0 }).to_string(),
13 => result.game_version.to_string(),
14 => result.likes.to_string(),
16 => (-result.likes).to_string(),
15 => result.length.to_string(),
17 => match difficulty {
Some(diff) => {
if diff == helpers::difficulty::LevelDifficulty::Demon {
1
} else {
0
}
},
None => 0
}.to_string(),
18 => (if let Some(stars) = result.stars { stars } else { 0 }).to_string(),
19 => result.featured.to_string(),
25 => match difficulty {
Some(diff) => {
if diff == helpers::difficulty::LevelDifficulty::Auto {
1
} else {
0
}
},
None => 0
}.to_string(),
27 => xor_pass,
28 => "1".to_string(), // unimplemented
29 => "1".to_string(), // unimplemented
30 => (if let Some(original) = result.original { original } else { 0 }).to_string(),
31 => result.two_player.to_string(),
35 => (if result.song_id >= 50 { result.song_id } else { 0 }).to_string(),
36 => String::from_utf8(if input.extras.is_some() { result.extra_data } else { Vec::new() }).expect("invalid utf-8 sequence"),
37 => result.coins.to_string(),
38 => result.rated_coins.to_string(),
39 => (if let Some(requested_stars) = result.requested_stars { requested_stars } else { 0 }).to_string(),
40 => result.has_ldm.to_string(),
41 => "".to_string(), // unimplemented
42 => result.epic.to_string(),
43 => match demon_difficulty {
Some(diff) => {
diff
},
None => helpers::difficulty::DemonDifficulty::Hard
}.to_demon_difficulty().to_string(),
44 => "0".to_string(), // unimplemented
45 => result.objects.to_string(),
46 => result.editor_time.to_string(),
47 => result.editor_time_copies.to_string()
}));
response.push(helpers::encryption::gen_solo(String::from_utf8(level_data.to_vec()).expect("invalid utf-8 sequence")));
let thing = [
result.user_id.to_string(),
result.stars.unwrap_or(0).to_string(),
match difficulty {
Some(diff) => {
if diff == helpers::difficulty::LevelDifficulty::Demon {
1
} else {
0
}
},
None => 0
}.to_string(),
result.id.to_string(),
result.rated_coins.to_string(),
result.featured.to_string(),
result.password.unwrap_or(String::new()).to_string(),
0.to_string()
];
response.push(helpers::encryption::gen_solo_2(thing.join(",")));
return status::Custom(Status::Ok, Box::leak(response.join("#").into_boxed_str()))
}

View file

@ -2,19 +2,53 @@ use rocket::form::Form;
use rocket::http::Status;
use rocket::response::status;
use diesel::prelude::*;
use base64::{Engine as _, engine::general_purpose};
use sqlx::Type;
use sqlx::{Encode, Sqlite, query_builder::QueryBuilder, Execute};
use crate::helpers;
use crate::db;
#[derive(Debug, sqlx::FromRow)]
struct DynQuery {
id: i64,
name: String,
user_id: i64,
description: String,
original: Option<i32>,
game_version: i32,
requested_stars: Option<i32>,
version: i32,
song_id: i32,
length: i32,
objects: i32,
coins: i32,
has_ldm: i32,
two_player: i32,
downloads: i32,
likes: i32,
difficulty: Option<i64>,
community_difficulty: Option<i64>,
demon_difficulty: Option<i64>,
stars: Option<i32>,
featured: i32,
epic: i32,
rated_coins: i32,
user_username: String,
user_udid: Option<String>,
user_account_id: Option<i64>,
user_registered: i32,
editor_time: i32,
editor_time_copies: i32
}
#[derive(FromForm)]
pub struct FormGetLevels {
page: i64,
str: String,
page: Option<i64>,
str: Option<String>,
accountID: Option<i32>,
accountID: Option<i64>,
gjp: Option<String>,
gjp2: Option<String>,
password: Option<String>,
@ -41,55 +75,57 @@ pub struct FormGetLevels {
}
#[post("/getGJLevels20.php", data = "<input>")]
pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
let connection = &mut db::establish_connection_pg();
use db::schema::{levels, users};
use db::models::{Level, User};
pub async fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
let mut connection = db::establish_sqlite_conn().await;
let mut can_see_unlisted = false;
let mut query = levels::table.into_boxed();
let mut count_query = levels::table.into_boxed();
if input.str != "" && input.r#type != Some(5) && input.r#type != Some(10) && input.r#type != Some(19) {
match input.str.parse::<i32>() {
Ok(matched_id) => {
// WHERE [...]
let mut query_params: Vec<&str> = vec![];
// Use this for binding on `query_params`
let mut query_params_bind: Vec<Box<dyn ToString + Send>> = vec![];
// ORDER BY [...]
let mut order = "levels.created_at DESC";
let page_offset = input.page.unwrap_or(0) * 10;
let search_query = input.str.clone().unwrap_or("".to_string());
if !search_query.is_empty() && input.r#type != Some(5) && input.r#type != Some(10) && input.r#type != Some(19) {
match search_query.parse::<i64>() {
Ok(id) => {
can_see_unlisted = true;
query = query.filter(levels::id.eq(matched_id));
count_query = count_query.filter(levels::id.eq(matched_id))
query_params.push("levels.id = ?");
query_params_bind.push(Box::new(id))
},
Err(_) => {
query = query.filter(levels::name.ilike(input.str.to_owned() + "%"));
count_query = count_query.filter(levels::name.ilike(input.str.to_owned() + "%"))
query_params.push("levels.name LIKE ?");
query_params_bind.push(Box::new(search_query.clone() + "%"));
}
}
}
if let Some(1) = input.featured {
query = query.filter(levels::featured.eq(1));
count_query = count_query.filter(levels::featured.eq(1))
query_params.push("featured = 1");
}
if let Some(1) = input.original {
query = query.filter(levels::original.is_null());
count_query = count_query.filter(levels::original.is_null())
query_params.push("original IS NULL");
}
if let Some(1) = input.coins {
query = query.filter(levels::rated_coins.eq(1).and(levels::coins.ne(0)));
count_query = count_query.filter(levels::rated_coins.eq(1).and(levels::coins.ne(0)))
query_params.push("rated_coins = 1 AND levels.coins != 0");
}
if let Some(1) = input.epic {
query = query.filter(levels::epic.eq(1));
count_query = count_query.filter(levels::epic.eq(1))
query_params.push("epic = 1");
}
if let Some(1) = input.uncompleted {
match input.completedLevels.clone() {
Some(completed_levels) => {
let clean_levels: Vec<i32> = completed_levels[1..completed_levels.len() - 1].split(',')
.map(|s| s.parse::<i32>().expect("failed to parse i32"))
let clean_levels: Vec<i64> = completed_levels[1..completed_levels.len() - 1].split(',')
.map(|s| s.parse::<i64>().expect("failed to parse i64"))
.collect();
query = query.filter(levels::id.ne_all(clean_levels.clone()));
count_query = count_query.filter(levels::id.ne_all(clean_levels))
let levels_str = clean_levels.iter().map(|n| n.to_string()).collect::<Vec<String>>().join(", ");
query_params.push("levels.id NOT IN (?)");
query_params_bind.push(Box::new(levels_str));
},
None => return status::Custom(Status::Ok, "-1")
}
@ -97,91 +133,92 @@ pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
if let Some(1) = input.onlyCompleted {
match input.completedLevels.clone() {
Some(completed_levels) => {
let clean_levels: Vec<i32> = completed_levels[1..completed_levels.len() - 1].split(',')
.map(|s| s.parse::<i32>().expect("failed to parse i32"))
let clean_levels: Vec<i64> = completed_levels[1..completed_levels.len() - 1].split(',')
.map(|s| s.parse::<i64>().expect("failed to parse i64"))
.collect();
query = query.filter(levels::id.eq_any(clean_levels.clone()));
count_query = count_query.filter(levels::id.eq_any(clean_levels))
let levels_str = clean_levels.iter().map(|n| n.to_string()).collect::<Vec<String>>().join(", ");
query_params.push("levels.id IN (?)");
query_params_bind.push(Box::new(levels_str));
},
None => return status::Custom(Status::Ok, "-1")
}
}
if let Some(song_id) = input.song {
if let Some(custom_song) = input.customSong {
query = query.filter(levels::song_id.eq(custom_song));
count_query = count_query.filter(levels::song_id.eq(custom_song))
query_params.push("song_id = ?");
query_params_bind.push(Box::new(custom_song));
} else {
query = query.filter(levels::song_id.eq(song_id));
count_query = count_query.filter(levels::song_id.eq(song_id));
query_params.push("song_id = ?");
query_params_bind.push(Box::new(song_id));
}
}
if let Some(1) = input.twoPlayer {
query = query.filter(levels::two_player.eq(1));
count_query = count_query.filter(levels::two_player.eq(1))
query_params.push("two_player = 1");
}
if let Some(1) = input.star {
query = query.filter(levels::stars.is_not_null());
count_query = count_query.filter(levels::stars.is_not_null())
query_params.push("levels.stars IS NOT NULL");
}
if let Some(1) = input.noStar {
query = query.filter(levels::stars.is_null());
count_query = count_query.filter(levels::stars.is_null())
query_params.push("levels.stars IS NULL");
}
if let Some(_gauntlet_id) = input.gauntlet {
unimplemented!("no gauntlet support")
}
if let Some(len) = input.len {
query = query.filter(levels::length.eq(len));
count_query = count_query.filter(levels::length.eq(len))
query_params.push("levels.length = ?");
query_params_bind.push(Box::new(len));
}
if let Some(diff) = input.diff.clone() {
if diff != "-" {
match diff.as_str() {
"-1" => {
query = query.filter(levels::difficulty.is_null().and(levels::community_difficulty.is_null()));
count_query = count_query.filter(levels::difficulty.is_null().and(levels::community_difficulty.is_null()))
query_params.push("difficulty IS NULL AND community_difficulty IS NULL"); // NA
},
"-2" => match input.demonFilter {
Some(demon_filter) => {
match demon_filter {
1 => {
query = query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Easy.to_demon_difficulty()));
count_query = count_query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Easy.to_demon_difficulty()))
query_params.push("demon_difficulty = ?");
query_params_bind.push(Box::new(helpers::difficulty::DemonDifficulty::Easy.to_demon_difficulty()));
},
2 => {
query = query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Medium.to_demon_difficulty()));
count_query = count_query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Medium.to_demon_difficulty()))
query_params.push("demon_difficulty = ?");
query_params_bind.push(Box::new(helpers::difficulty::DemonDifficulty::Medium.to_demon_difficulty()));
},
3 => {
query = query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Hard.to_demon_difficulty()));
count_query = count_query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Hard.to_demon_difficulty()))
query_params.push("demon_difficulty = ?");
query_params_bind.push(Box::new(helpers::difficulty::DemonDifficulty::Hard.to_demon_difficulty()));
},
4 => {
query = query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Insane.to_demon_difficulty()));
count_query = count_query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Insane.to_demon_difficulty()))
query_params.push("demon_difficulty = ?");
query_params_bind.push(Box::new(helpers::difficulty::DemonDifficulty::Insane.to_demon_difficulty()));
},
5 => {
query = query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Extreme.to_demon_difficulty()));
count_query = count_query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Extreme.to_demon_difficulty()))
query_params.push("demon_difficulty = ?");
query_params_bind.push(Box::new(helpers::difficulty::DemonDifficulty::Extreme.to_demon_difficulty()));
},
_ => panic!("invalid demon filter!")
}
query = query.filter(diesel::BoolExpressionMethods::or(levels::difficulty.eq::<i32>(helpers::difficulty::LevelDifficulty::Demon.to_star_difficulty()), levels::difficulty.is_null().and(levels::community_difficulty.eq::<i32>(helpers::difficulty::LevelDifficulty::Demon.to_star_difficulty()))));
count_query = count_query.filter(diesel::BoolExpressionMethods::or(levels::difficulty.eq::<i32>(helpers::difficulty::LevelDifficulty::Demon.to_star_difficulty()), levels::difficulty.is_null().and(levels::community_difficulty.eq::<i32>(helpers::difficulty::LevelDifficulty::Demon.to_star_difficulty()))))
query_params.push("difficulty = ? OR (difficulty IS NULL AND community_difficulty = ?)");
query_params_bind.push(Box::new(helpers::difficulty::LevelDifficulty::Demon.to_star_difficulty()));
query_params_bind.push(Box::new(helpers::difficulty::LevelDifficulty::Demon.to_star_difficulty()));
},
None => panic!("demon filter option with no demon filter argument")
},
"-3" => {
query = query.filter(diesel::BoolExpressionMethods::or(levels::difficulty.eq::<i32>(helpers::difficulty::LevelDifficulty::Auto.to_star_difficulty()), levels::difficulty.is_null().and(levels::community_difficulty.eq::<i32>(helpers::difficulty::LevelDifficulty::Auto.to_star_difficulty()))));
count_query = count_query.filter(diesel::BoolExpressionMethods::or(levels::difficulty.eq::<i32>(helpers::difficulty::LevelDifficulty::Auto.to_star_difficulty()), levels::difficulty.is_null().and(levels::community_difficulty.eq::<i32>(helpers::difficulty::LevelDifficulty::Auto.to_star_difficulty()))))
query_params.push("difficulty = ? OR (difficulty IS NULL AND community_difficulty = ?)");
query_params_bind.push(Box::new(helpers::difficulty::LevelDifficulty::Auto.to_star_difficulty()));
query_params_bind.push(Box::new(helpers::difficulty::LevelDifficulty::Auto.to_star_difficulty()));
},
// easy, normal, hard, harder, insane
_ => {
let diffs: Vec<i32> = diff.split(',')
let clean_diffs: Vec<i32> = diff.split(',')
.map(|v| v.parse::<i32>().expect("couldnt parse i32"))
.collect();
query = query.filter(levels::difficulty.eq_any(diffs.clone()).or(levels::difficulty.is_null().and(levels::community_difficulty.eq_any(diffs.clone()))));
count_query = count_query.filter(levels::difficulty.eq_any(diffs.clone()).or(levels::difficulty.is_null().and(levels::community_difficulty.eq_any(diffs))))
let diffs_str = clean_diffs.iter().map(|n| n.to_string()).collect::<Vec<String>>().join(", ");
query_params.push("difficulty IN (?) OR (difficulty IS NULL AND community_difficulty IN (?))");
query_params_bind.push(Box::new(diffs_str.clone()));
query_params_bind.push(Box::new(diffs_str));
}
}
}
@ -191,13 +228,11 @@ pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
match search_type {
// downloads
1 => {
query = query.order(levels::downloads.desc());
// count query order doesnt matter
order = "levels.downloads DESC";
},
// likes
2 => {
query = query.order(levels::likes.desc());
// count query order doesnt matter
order = "levels.likes DESC";
},
// trending
3 => {
@ -212,9 +247,9 @@ pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
5 => {
if let Some(1) = input.local {
if let Some(input_account_id) = input.accountID {
let (user_id_val, _account_id_val): (i32, i32);
let (user_id_val, _account_id_val): (i64, i64);
match helpers::accounts::auth(input_account_id, input.password.clone(), input.gjp.clone(), input.gjp2.clone()) {
match helpers::accounts::auth(input_account_id, input.password.clone(), input.gjp.clone(), input.gjp2.clone()).await {
Ok((user_id_val_auth, account_id_val_auth)) => {
user_id_val = user_id_val_auth;
_account_id_val = account_id_val_auth;
@ -222,37 +257,34 @@ pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
Err(_) => return status::Custom(Status::Ok, "-1")
};
if user_id_val == input.str.parse::<i32>().expect("couldnt convert query input to i32") {
if user_id_val == search_query.parse::<i64>().expect("couldnt convert query input to i64") {
can_see_unlisted = true;
query = query.filter(levels::user_id.eq(user_id_val));
count_query = count_query.filter(levels::user_id.eq(user_id_val))
query_params.push("levels.user_id = ?");
query_params_bind.push(Box::new(user_id_val));
} else {
return status::Custom(Status::Ok, "-1")
}
}
}
if let None = input.local {
let user_id_val = input.str.parse::<i32>().expect("couldnt convert query input to i32");
let user_id_val = search_query.parse::<i64>().expect("couldnt convert query input to i64");
query = query.filter(levels::user_id.eq(user_id_val));
count_query = count_query.filter(levels::user_id.eq(user_id_val))
query_params.push("levels.user_id = ?");
query_params_bind.push(Box::new(user_id_val));
}
}
// featured
// 17 is gdworld
6 | 17 => {
query = query.filter(levels::featured.eq(1));
count_query = count_query.filter(levels::featured.eq(1))
query_params.push("featured = 1");
},
// epic / HoF
16 => {
query = query.filter(levels::epic.eq(1));
count_query = count_query.filter(levels::epic.eq(1))
query_params.push("epic = 1");
},
// magic
7 => {
query = query.filter(levels::objects.gt(4000));
count_query = count_query.filter(levels::objects.gt(4000))
query_params.push("objects > 4000");
},
// map packs 🙄😶
10 | 19 => {
@ -260,8 +292,7 @@ pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
},
// rated
11 => {
query = query.filter(levels::stars.is_not_null());
count_query = count_query.filter(levels::stars.is_not_null())
query_params.push("levels.stars IS NOT NULL");
},
// followed
12 => {
@ -286,72 +317,63 @@ pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
// default sort
// 15 is gdworld
0 | 15 | _ => {
query = query.order(levels::likes.desc());
// count query order doesnt matter
order = "likes DESC";
},
}
}
if !can_see_unlisted {
query = query.filter(levels::unlisted.eq(0));
count_query = count_query.filter(levels::unlisted.eq(0))
query_params.push("unlisted = 0");
}
let mut results: Vec<String> = [].to_vec();
let mut users: Vec<String> = [].to_vec();
let mut songs: Vec<String> = [].to_vec();
let where_str = format!("WHERE ({})", query_params.join(" AND "));
let query_base = format!("FROM levels JOIN users ON levels.user_id = users.id {} ORDER BY {}", where_str, order);
let mut hash_data: Vec<(i32, i32, bool)> = [].to_vec();
let mut query_builder: QueryBuilder<Sqlite> = QueryBuilder::new(&format!("SELECT levels.id, levels.name, users.id as user_id, levels.description, levels.original, levels.game_version, levels.requested_stars, levels.version, levels.song_id, levels.length, levels.objects, levels.coins, levels.has_ldm, levels.two_player, levels.downloads, levels.likes, levels.difficulty, levels.community_difficulty, levels.demon_difficulty, levels.stars, levels.featured, levels.epic, levels.rated_coins, users.username as user_username, users.udid as user_udid, users.account_id as user_account_id, users.registered as user_registered, editor_time, editor_time_copies {}", query_base));
let mut query = query_builder.build_query_as::<DynQuery>();
for param in query_params_bind {
query = query.bind(param.to_string());
}
let mut results: Vec<String> = vec![];
let mut users: Vec<String> = vec![];
let mut songs: Vec<String> = vec![];
let mut hash_data: Vec<(i64, i32, bool)> = vec![];
let count: i64 = sqlx::query_scalar(&format!("SELECT COUNT(*) {}", query_base))
.fetch_one(&mut connection)
.await
.expect("error getting level count");
// for goop in {
// query
// .fetch_all(&mut connection)
// .await
// .expect("error loading levels")
// } {
// println!("lvl id: {:?}", goop.id);
// println!("user id: {:?}", goop.user_id);
// }
for result in {
query
.order(levels::created_at.desc())
.offset(input.page * 10)
.limit(10)
.get_results::<Level, >(connection)
.expect("fatal error loading levels")
.fetch_all(&mut connection)
.await
.expect("error loading levels")
} {
let user: User = users::table.find(result.user_id).get_result::<User, >(connection).expect("couldnt get user from lvl");
let level: Level = result;
let set_difficulty = match level.difficulty {
Some(diff) => {
Some(helpers::difficulty::LevelDifficulty::new(diff))
},
None => None
};
let community_difficulty = match level.community_difficulty {
Some(diff) => {
Some(helpers::difficulty::LevelDifficulty::new(diff))
},
None => None
};
let difficulty = match set_difficulty {
Some(diff) => {
Some(diff)
},
None => {
match community_difficulty {
Some(diff) => {
Some(diff)
},
None => None
}
}
};
let demon_difficulty = match level.demon_difficulty {
Some(diff) => {
Some(helpers::difficulty::DemonDifficulty::new(diff))
},
None => None
};
let set_difficulty = result.difficulty.map(helpers::difficulty::LevelDifficulty::new);
let community_difficulty = result.community_difficulty.map(helpers::difficulty::LevelDifficulty::new);
let difficulty = set_difficulty.or(community_difficulty);
let demon_difficulty = result.demon_difficulty.map(helpers::difficulty::DemonDifficulty::new);
results.push(helpers::format::format(hashmap! {
1 => level.id.to_string(),
2 => level.name,
3 => general_purpose::URL_SAFE.encode(level.description),
5 => level.version.to_string(),
6 => user.id.to_string(),
1 => result.id.to_string(),
2 => result.name,
3 => general_purpose::URL_SAFE.encode(result.description),
5 => result.version.to_string(),
6 => result.user_id.to_string(),
// this argument is weird. its the "difficulty divisor"
// used to be vote count but yeah
8 => 10.to_string(),
@ -359,12 +381,12 @@ pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
Some(diff) => diff.to_star_difficulty(),
None => 0
} * 10).to_string(),
10 => level.downloads.to_string(),
12 => (if level.song_id < 50 { level.song_id } else { 0 }).to_string(),
13 => level.game_version.to_string(),
14 => level.likes.to_string(),
16 => (-level.likes).to_string(),
15 => level.length.to_string(),
10 => result.downloads.to_string(),
12 => (if result.song_id < 50 { result.song_id } else { 0 }).to_string(),
13 => result.game_version.to_string(),
14 => result.likes.to_string(),
16 => (-result.likes).to_string(),
15 => result.length.to_string(),
17 => match difficulty {
Some(diff) => {
if diff == helpers::difficulty::LevelDifficulty::Demon {
@ -375,8 +397,8 @@ pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
},
None => 0
}.to_string(),
18 => (if let Some(stars) = level.stars { stars } else { 0 }).to_string(),
19 => level.featured.to_string(),
18 => (if let Some(stars) = result.stars { stars } else { 0 }).to_string(),
19 => result.featured.to_string(),
25 => match difficulty {
Some(diff) => {
if diff == helpers::difficulty::LevelDifficulty::Auto {
@ -387,54 +409,49 @@ pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
},
None => 0
}.to_string(),
30 => (if let Some(original) = level.original { original } else { 0 }).to_string(),
31 => level.two_player.to_string(),
35 => (if level.song_id >= 50 { level.song_id } else { 0 }).to_string(),
37 => level.coins.to_string(),
38 => level.rated_coins.to_string(),
39 => (if let Some(requested_stars) = level.requested_stars { requested_stars } else { 0 }).to_string(),
40 => level.has_ldm.to_string(),
42 => level.epic.to_string(),
30 => (if let Some(original) = result.original { original } else { 0 }).to_string(),
31 => result.two_player.to_string(),
35 => (if result.song_id >= 50 { result.song_id } else { 0 }).to_string(),
37 => result.coins.to_string(),
38 => result.rated_coins.to_string(),
39 => (if let Some(requested_stars) = result.requested_stars { requested_stars } else { 0 }).to_string(),
40 => result.has_ldm.to_string(),
42 => result.epic.to_string(),
43 => match demon_difficulty {
Some(diff) => {
diff
},
None => helpers::difficulty::DemonDifficulty::Hard
}.to_demon_difficulty().to_string(),
45 => level.objects.to_string(),
46 => level.editor_time.to_string(),
47 => level.editor_time_copies.to_string()
45 => result.objects.to_string(),
46 => result.editor_time.to_string(),
47 => result.editor_time_copies.to_string()
}));
users.push(format!("{}:{}:{}", user.id, user.username, {
if user.registered == 1 {
user.account_id.expect("wtf? registered user with no account id.").to_string()
users.push(format!("{}:{}:{}", result.user_id, result.user_username, {
if result.user_registered == 1 {
result.user_account_id.expect("wtf? registered user with no account id.").to_string()
} else {
user.udid.expect("wtf? unregistered user with no udid.")
result.user_udid.expect("wtf? unregistered user with no udid.")
}
}));
hash_data.push((
level.id,
{ if let Some(stars) = level.stars {
result.id,
{ if let Some(stars) = result.stars {
stars
} else {
0
}},
{ if let 1 = level.rated_coins {
{ if let 1 = result.rated_coins {
true
} else {
false
}}
));
};
}
let level_count = count_query
.count()
.get_result::<i64, >(connection)
.expect("failed to get count of levels");
let search_meta = format!("{}:{}:{}", level_count, input.page * 10, 10);
let search_meta = format!("{}:{}:{}", count, page_offset, 10);
let response = vec![results.join("|"), users.join("|"), songs.join("|"), search_meta, helpers::encryption::gen_multi(hash_data)].join("#");

View file

@ -2,8 +2,6 @@ use rocket::form::Form;
use rocket::http::Status;
use rocket::response::status;
use diesel::prelude::*;
use base64::{Engine as _, engine::general_purpose};
use std::fs;
@ -14,7 +12,7 @@ use crate::db;
#[derive(FromForm)]
pub struct FormUploadLevel {
accountID: i32,
accountID: i64,
gjp: Option<String>,
gjp2: Option<String>,
@ -24,7 +22,7 @@ pub struct FormUploadLevel {
audioTrack: i32,
levelName: String,
levelDesc: String,
levelID: i32,
levelID: i64,
levelVersion: i32,
levelInfo: String,
levelString: String,
@ -40,14 +38,14 @@ pub struct FormUploadLevel {
}
#[post("/uploadGJLevel21.php", data = "<input>")]
pub fn upload_level(input: Form<FormUploadLevel>) -> status::Custom<&'static str> {
let connection = &mut db::establish_connection_pg();
pub async fn upload_level(input: Form<FormUploadLevel>) -> status::Custom<&'static str> {
let mut connection = db::establish_sqlite_conn().await;
// account verification
let (user_id_val, _account_id_val): (i32, i32);
let (user_id_val, _account_id_val): (i64, i64);
// password argument is used for the level, so
match helpers::accounts::auth(input.accountID.clone(), None, input.gjp.clone(), input.gjp2.clone()) {
match helpers::accounts::auth(input.accountID.clone(), None, input.gjp.clone(), input.gjp2.clone()).await {
Ok((user_id, account_id)) => {
user_id_val = user_id;
_account_id_val = account_id;
@ -91,112 +89,93 @@ pub fn upload_level(input: Form<FormUploadLevel>) -> status::Custom<&'static str
let objects_val = level_objects.len();
let two_player_val = if inner_level_string.get("kA10").unwrap_or(&String::from("0")).parse::<i32>().expect("kA10 not int") == 1 { 1 } else { 0 };
let level_length_val = helpers::levels::secs_to_time(level_length_secs);
// blocking coins
if coins_val > 3 {
return status::Custom(Status::Ok, "-1")
}
// too many objects
if config::config_get_with_default("levels.max_objects", 0) != 0 && objects_val > config::config_get_with_default("levels.max_objects", 0) {
let max_objects = config::config_get_with_default("levels.max_objects", 0) as usize;
if max_objects != 0 && objects_val > max_objects {
return status::Custom(Status::Ok, "-1")
}
// forbidden object checking
if let Some(_forbidden_object) = level_objects.iter().find(|obj| config::config_get_with_default("levels.blocklist", Vec::new() as Vec<i32>).contains(&obj.id())) {
if let Some(_obj) = level_objects.iter().find(|obj| config::config_get_with_default("levels.blocklist", Vec::new() as Vec<i32>).contains(&obj.id())) {
return status::Custom(Status::Ok, "-1")
}
// ACE vulnerability check
if let Some(_ace_object) = level_objects.iter().find(|obj| obj.item_block_id() < Some(0) || obj.item_block_id() > Some(1100)) {
return status::Custom(Status::Ok, "-1")
}
// data base 🤣😁
{
use db::models::{Level, NewLevel};
use db::schema::levels::dsl::*;
if levels
.filter(id.eq(input.levelID))
.count()
.get_result::<i64>(connection)
.expect("couldnt get count of levels") > 0 {
// update level
let level_user_id = levels
.filter(id.eq(input.levelID))
.select(user_id)
.get_result::<i32>(connection)
.expect("couldnt query levels");
if level_user_id != user_id_val {
return status::Custom(Status::Ok, "-1")
}
let updated_level = diesel::update(levels)
.filter(id.eq(input.levelID))
.set((
description.eq(description_val.chars().take(140).collect::<String>()),
password.eq(input.password.clone()),
requested_stars.eq(match input.requestedStars {
Some(requested_stars_val) => requested_stars_val.clamp(0, 10),
None => 0
}),
version.eq(input.levelVersion),
extra_data.eq(extra_string.as_bytes().to_owned()),
level_info.eq(input.levelInfo.clone().into_bytes()),
editor_time.eq(input.wt.unwrap_or(0)),
editor_time_copies.eq(input.wt2.unwrap_or(0)),
song_id.eq(song_id_val),
length.eq(level_length_val),
objects.eq(objects_val as i32),
coins.eq(coins_val as i32),
has_ldm.eq(input.ldm.unwrap_or(0).clamp(0, 1)),
two_player.eq(two_player_val)
))
.get_result::<Level, >(connection)
.expect("failed to update level");
fs::write(format!("{}/levels/{}.lvl", config::config_get_with_default("db.data_folder", "data"), updated_level.id), general_purpose::URL_SAFE.decode(input.levelString.clone()).expect("user provided invalid level string")).expect("couldnt write level to file");
return status::Custom(Status::Ok, Box::leak(input.levelID.to_string().into_boxed_str()))
} else {
// upload level
let new_level = NewLevel {
name: helpers::clean::clean_basic(&input.levelName).chars().take(20).collect(),
user_id: user_id_val,
description: description_val.chars().take(140).collect(),
original: input.original,
game_version: input.gameVersion,
binary_version: input.binaryVersion.unwrap_or(0),
password: input.password.clone(),
requested_stars: match input.requestedStars {
Some(requested_stars_val) => requested_stars_val.clamp(0, 10),
None => 0
},
unlisted: input.unlisted.unwrap_or(0).clamp(0, 1),
version: input.levelVersion,
extra_data: extra_string.as_bytes().to_owned(),
level_info: input.levelInfo.clone().into_bytes(),
editor_time: input.wt.unwrap_or(0),
editor_time_copies: input.wt2.unwrap_or(0),
song_id: song_id_val,
length: level_length_val,
objects: objects_val as i32,
coins: coins_val as i32,
has_ldm: input.ldm.unwrap_or(0).clamp(0, 1),
two_player: two_player_val
};
let inserted_level = diesel::insert_into(levels)
.values(&new_level)
.get_result::<Level, >(connection)
.expect("failed to insert level");
fs::write(format!("{}/levels/{}.lvl", config::config_get_with_default("db.data_folder", "data"), inserted_level.id), general_purpose::URL_SAFE.decode(input.levelString.clone()).expect("user provided invalid level string")).expect("couldnt write level to file");
return status::Custom(Status::Ok, "1")
}
// ACE vulnerability check
for obj in level_objects.iter().filter(|obj| obj.item_block_id().is_some()) {
if obj.item_block_id() < Some(0) || obj.item_block_id() > Some(1100) {
return status::Custom(Status::Ok, "-1");
}
}
if sqlx::query_scalar!("SELECT COUNT(*) FROM levels WHERE id = ?", input.levelID)
.fetch_one(&mut connection)
.await
.expect("error getting level count") > 0 {
// update level
let level_user_id = sqlx::query!("SELECT user_id FROM levels WHERE id = ?", input.levelID)
.fetch_one(&mut connection)
.await
.expect("error getting level user id")
.user_id;
if level_user_id != user_id_val {
return status::Custom(Status::Ok, "-1")
}
let new_description = description_val.chars().take(140).collect::<String>();
let new_password = input.password.clone();
let new_requested_stars = match input.requestedStars { Some(requested_stars_val) => requested_stars_val.clamp(0, 10), None => 0 };
let new_extra_string = extra_string.as_bytes().to_owned();
let new_level_info = input.levelInfo.clone().into_bytes();
let new_editor_time = input.wt.unwrap_or(0);
let new_editor_time_copies = input.wt2.unwrap_or(0);
let new_objects = objects_val as i64;
let new_coins = coins_val as i64;
let new_ldm = input.ldm.unwrap_or(0).clamp(0, 1);
let updated_level = sqlx::query!("UPDATE levels SET description = ?, password = ?, requested_stars = ?, version = ?, extra_data = ?, level_info = ?, editor_time = ?, editor_time_copies = ?, song_id = ?, length = ?, objects = ?, coins = ?, has_ldm = ?, two_player = ? WHERE id = ?",new_description, new_password, new_requested_stars, input.levelVersion, new_extra_string, new_level_info, new_editor_time, new_editor_time_copies, song_id_val, level_length_val, new_objects, new_coins, new_ldm, two_player_val, input.levelID)
.execute(&mut connection)
.await
.expect("error updating level");
let updated_level_id = updated_level.last_insert_rowid();
fs::write(format!("{}/levels/{}.lvl", config::config_get_with_default("db.data_folder", "data".to_string()), updated_level_id), general_purpose::URL_SAFE.decode(input.levelString.clone()).expect("user provided invalid level string")).expect("couldnt write level to file");
return status::Custom(Status::Ok, Box::leak(input.levelID.to_string().into_boxed_str()))
} else {
// insert level
let new_name = helpers::clean::clean_basic(&input.levelName).chars().take(20).collect::<String>();
let new_description = description_val.chars().take(140).collect::<String>();
let new_binary_version = input.binaryVersion.unwrap_or(0);
let new_password = input.password.clone();
let new_requested_stars = match input.requestedStars { Some(requested_stars_val) => requested_stars_val.clamp(0, 10), None => 0 };
let new_unlisted = input.unlisted.unwrap_or(0).clamp(0, 1);
let new_extra_string = extra_string.as_bytes().to_owned();
let new_level_info = input.levelInfo.clone().into_bytes();
let new_editor_time = input.wt.unwrap_or(0);
let new_editor_time_copies = input.wt2.unwrap_or(0);
let new_objects = objects_val as i64;
let new_coins = coins_val as i64;
let new_ldm = input.ldm.unwrap_or(0).clamp(0, 1);
let inserted_level = sqlx::query!("INSERT INTO levels (name, user_id, description, original, game_version, binary_version, password, requested_stars, unlisted, version, extra_data, level_info, editor_time, editor_time_copies, song_id, length, objects, coins, has_ldm, two_player) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", new_name, user_id_val, new_description, input.original, input.gameVersion, new_binary_version, new_password, new_requested_stars, new_unlisted, input.levelVersion, new_extra_string, new_level_info, new_editor_time, new_editor_time_copies, song_id_val, level_length_val, new_objects, new_coins, new_ldm, two_player_val)
.execute(&mut connection)
.await
.expect("error inserting level");
let inserted_level_id = inserted_level.last_insert_rowid();
fs::write(format!("{}/levels/{}.lvl", config::config_get_with_default("db.data_folder", "data".to_string()), inserted_level_id), general_purpose::URL_SAFE.decode(input.levelString.clone()).expect("user provided invalid level string")).expect("couldnt write level to file");
return status::Custom(Status::Ok, Box::leak(inserted_level_id.to_string().into_boxed_str()))
}
}

View file

@ -2,8 +2,6 @@ use rocket::form::Form;
use rocket::http::Status;
use rocket::response::status;
use diesel::prelude::*;
use crate::helpers;
use crate::db;
@ -14,31 +12,21 @@ pub struct FormGetUsers {
}
#[post("/getGJUsers20.php", data = "<input>")]
pub fn get_users(input: Form<FormGetUsers>) -> status::Custom<&'static str> {
let connection = &mut db::establish_connection_pg();
pub async fn get_users(input: Form<FormGetUsers>) -> status::Custom<&'static str> {
let mut connection = db::establish_sqlite_conn().await;
// query users
use db::schema::users::dsl::*;
use db::models::User;
let username = input.str.to_owned() + "%";
let offset = input.page * 10;
let mut query_users = users.into_boxed();
match input.str.parse::<i32>() {
Ok(id_value) => query_users = query_users.filter(id.eq(id_value)),
Err(_) => query_users = query_users.filter(username.ilike(input.str.to_owned() + "%"))
};
let query_results = sqlx::query!("SELECT * FROM users WHERE id = ? OR username LIKE ? ORDER BY stars DESC LIMIT 10 OFFSET ?", input.str, username, offset)
.fetch_all(&mut connection)
.await
.expect("Fatal error loading users");
let mut results: Vec<String> = vec![];
for result in {
query_users
.order(stars.desc())
.offset(input.page * 10)
.limit(10)
.get_results::<User, >(connection)
.expect("Fatal error loading users")
} {
let user: User = result;
for result in query_results {
let user = result;
let formatted_result = helpers::format::format(hashmap! {
1 => user.username,
@ -75,22 +63,15 @@ pub fn get_users(input: Form<FormGetUsers>) -> status::Custom<&'static str> {
results.push(formatted_result)
};
let mut query_users_count = users.into_boxed();
match input.str.parse::<i32>() {
Ok(id_value) => query_users_count = query_users_count.filter(id.eq(id_value)),
Err(_) => query_users_count = query_users_count.filter(username.ilike(input.str.to_owned() + "%"))
};
let amount = query_users_count
.count()
.get_result::<i64>(connection)
.expect("error querying user count");
let amount = sqlx::query_scalar!("SELECT COUNT(*) FROM users WHERE id = ? OR username LIKE ?", input.str, username)
.fetch_one(&mut connection)
.await
.expect("error loading users");
let response = if results.is_empty() {
String::from("-1")
} else {
vec![results.join("|"), format!("{}:{}:10", amount, input.page * 10)].join("#")
vec![results.join("|"), format!("{}:{}:10", amount, offset)].join("#")
};
return status::Custom(Status::Ok, Box::leak(response.into_boxed_str()))