diff --git a/gen/new_server_match.c b/gen/new_server_match.c index 35d3bbdb8..b39854f0e 100644 --- a/gen/new_server_match.c +++ b/gen/new_server_match.c @@ -574,43 +574,27 @@ int ns_match_action(const unsigned char *str) c = str[1]; if (c == 'o') { c = str[2]; - if (c == 'n') { + if (c == 'o') { c = str[3]; - if (c == 't') { + if (c == 'k') { c = str[4]; if (c == 'i') { c = str[5]; - if (c == 'n') { + if (c == 'e') { c = str[6]; - if (c == 'u') { + if (c == '-') { c = str[7]; - if (c == 'e') { + if (c == 'l') { c = str[8]; - if (c == '-') { + if (c == 'o') { c = str[9]; - if (c == 'c') { + if (c == 'g') { c = str[10]; - if (c == 'o') { + if (c == 'i') { c = str[11]; if (c == 'n') { c = str[12]; - if (c == 't') { - c = str[13]; - if (c == 'e') { - c = str[14]; - if (c == 's') { - c = str[15]; - if (c == 't') { - c = str[16]; - if (!c) return NEW_SRV_ACTION_CONTINUE_CONTEST; - return 0; - } - return 0; - } - return 0; - } - return 0; - } + if (!c) return NEW_SRV_ACTION_COOKIE_LOGIN; return 0; } return 0; @@ -626,26 +610,46 @@ int ns_match_action(const unsigned char *str) return 0; } return 0; - } else if (c < 'i') { - if (c == 'e') { + } + return 0; + } + return 0; + } else if (c < 'o') { + if (c == 'n') { + c = str[3]; + if (c == 't') { + c = str[4]; + if (c == 'i') { c = str[5]; - if (c == 's') { + if (c == 'n') { c = str[6]; - if (c == 't') { + if (c == 'u') { c = str[7]; - if (c == 's') { + if (c == 'e') { c = str[8]; if (c == '-') { c = str[9]; - if (c == 'p') { + if (c == 'c') { c = str[10]; - if (c == 'a') { + if (c == 'o') { c = str[11]; - if (c == 'g') { + if (c == 'n') { c = str[12]; - if (c == 'e') { + if (c == 't') { c = str[13]; - if (!c) return NEW_SRV_ACTION_CONTESTS_PAGE; + if (c == 'e') { + c = str[14]; + if (c == 's') { + c = str[15]; + if (c == 't') { + c = str[16]; + if (!c) return NEW_SRV_ACTION_CONTINUE_CONTEST; + return 0; + } + return 0; + } + return 0; + } return 0; } return 0; @@ -657,36 +661,32 @@ int ns_match_action(const unsigned char *str) return 0; } return 0; - } else if (c < 's') { - if (c == '-') { + } + return 0; + } + return 0; + } + return 0; + } else if (c < 'i') { + if (c == 'e') { + c = str[5]; + if (c == 's') { + c = str[6]; + if (c == 't') { + c = str[7]; + if (c == 's') { c = str[8]; - if (c == 'i') { + if (c == '-') { c = str[9]; - if (c == 'n') { + if (c == 'p') { c = str[10]; - if (c == 'f') { + if (c == 'a') { c = str[11]; - if (c == 'o') { + if (c == 'g') { c = str[12]; - if (c == '-') { + if (c == 'e') { c = str[13]; - if (c == 'j') { - c = str[14]; - if (c == 's') { - c = str[15]; - if (c == 'o') { - c = str[16]; - if (c == 'n') { - c = str[17]; - if (!c) return NEW_SRV_ACTION_CONTEST_INFO_JSON; - return 0; - } - return 0; - } - return 0; - } - return 0; - } + if (!c) return NEW_SRV_ACTION_CONTESTS_PAGE; return 0; } return 0; @@ -696,18 +696,38 @@ int ns_match_action(const unsigned char *str) return 0; } return 0; - } else if (c < 'i') { - if (c == 'b') { + } + return 0; + } else if (c < 's') { + if (c == '-') { + c = str[8]; + if (c == 'i') { c = str[9]; - if (c == 'a') { + if (c == 'n') { c = str[10]; - if (c == 't') { + if (c == 'f') { c = str[11]; - if (c == 'c') { + if (c == 'o') { c = str[12]; - if (c == 'h') { + if (c == '-') { c = str[13]; - if (!c) return NEW_SRV_ACTION_CONTEST_BATCH; + if (c == 'j') { + c = str[14]; + if (c == 's') { + c = str[15]; + if (c == 'o') { + c = str[16]; + if (c == 'n') { + c = str[17]; + if (!c) return NEW_SRV_ACTION_CONTEST_INFO_JSON; + return 0; + } + return 0; + } + return 0; + } + return 0; + } return 0; } return 0; @@ -717,31 +737,54 @@ int ns_match_action(const unsigned char *str) return 0; } return 0; - } - } else { - if (c == 's') { - c = str[9]; - if (c == 't') { - c = str[10]; + } else if (c < 'i') { + if (c == 'b') { + c = str[9]; if (c == 'a') { - c = str[11]; + c = str[10]; if (c == 't') { - c = str[12]; - if (c == 'u') { - c = str[13]; - if (c == 's') { - c = str[14]; - if (c == '-') { - c = str[15]; - if (c == 'j') { - c = str[16]; - if (c == 's') { - c = str[17]; - if (c == 'o') { - c = str[18]; - if (c == 'n') { - c = str[19]; - if (!c) return NEW_SRV_ACTION_CONTEST_STATUS_JSON; + c = str[11]; + if (c == 'c') { + c = str[12]; + if (c == 'h') { + c = str[13]; + if (!c) return NEW_SRV_ACTION_CONTEST_BATCH; + return 0; + } + return 0; + } + return 0; + } + return 0; + } + return 0; + } + } else { + if (c == 's') { + c = str[9]; + if (c == 't') { + c = str[10]; + if (c == 'a') { + c = str[11]; + if (c == 't') { + c = str[12]; + if (c == 'u') { + c = str[13]; + if (c == 's') { + c = str[14]; + if (c == '-') { + c = str[15]; + if (c == 'j') { + c = str[16]; + if (c == 's') { + c = str[17]; + if (c == 'o') { + c = str[18]; + if (c == 'n') { + c = str[19]; + if (!c) return NEW_SRV_ACTION_CONTEST_STATUS_JSON; + return 0; + } return 0; } return 0; @@ -762,46 +805,46 @@ int ns_match_action(const unsigned char *str) } return 0; } - return 0; } + return 0; } - return 0; + } else { } - } else { + return 0; } return 0; } return 0; } - return 0; + } else { } - } else { - } - return 0; - } else if (c < 't') { - if (c == 'f') { - c = str[4]; - if (c == 'i') { - c = str[5]; - if (c == 'r') { - c = str[6]; - if (c == 'm') { - c = str[7]; - if (c == '-') { - c = str[8]; - if (c == 'a') { - c = str[9]; - if (c == 'v') { - c = str[10]; - if (c == 'a') { - c = str[11]; - if (c == 't') { - c = str[12]; - if (c == 'a') { - c = str[13]; - if (c == 'r') { - c = str[14]; - if (!c) return NEW_SRV_ACTION_CONFIRM_AVATAR; + return 0; + } else if (c < 't') { + if (c == 'f') { + c = str[4]; + if (c == 'i') { + c = str[5]; + if (c == 'r') { + c = str[6]; + if (c == 'm') { + c = str[7]; + if (c == '-') { + c = str[8]; + if (c == 'a') { + c = str[9]; + if (c == 'v') { + c = str[10]; + if (c == 'a') { + c = str[11]; + if (c == 't') { + c = str[12]; + if (c == 'a') { + c = str[13]; + if (c == 'r') { + c = str[14]; + if (!c) return NEW_SRV_ACTION_CONFIRM_AVATAR; + return 0; + } return 0; } return 0; @@ -822,31 +865,31 @@ int ns_match_action(const unsigned char *str) } return 0; } - return 0; + } else { } - } else { - } - return 0; - } else if (c < 'n') { - if (c == 'm') { - c = str[3]; - if (c == 'p') { - c = str[4]; - if (c == 'i') { - c = str[5]; - if (c == 'l') { - c = str[6]; - if (c == 'e') { - c = str[7]; - if (c == 'r') { - c = str[8]; - if (c == '-') { - c = str[9]; - if (c == 'o') { - c = str[10]; - if (c == 'p') { - c = str[11]; - if (!c) return NEW_SRV_ACTION_COMPILER_OP; + return 0; + } else if (c < 'n') { + if (c == 'm') { + c = str[3]; + if (c == 'p') { + c = str[4]; + if (c == 'i') { + c = str[5]; + if (c == 'l') { + c = str[6]; + if (c == 'e') { + c = str[7]; + if (c == 'r') { + c = str[8]; + if (c == '-') { + c = str[9]; + if (c == 'o') { + c = str[10]; + if (c == 'p') { + c = str[11]; + if (!c) return NEW_SRV_ACTION_COMPILER_OP; + return 0; + } return 0; } return 0; @@ -858,26 +901,26 @@ int ns_match_action(const unsigned char *str) return 0; } return 0; - } - return 0; - } else if (c < 'i') { - if (c == 'a') { - c = str[5]; - if (c == 'r') { - c = str[6]; - if (c == 'e') { - c = str[7]; - if (c == '-') { - c = str[8]; - if (c == 'r') { - c = str[9]; - if (c == 'u') { - c = str[10]; - if (c == 'n') { - c = str[11]; - if (c == 's') { - c = str[12]; - if (!c) return NEW_SRV_ACTION_COMPARE_RUNS; + } else if (c < 'i') { + if (c == 'a') { + c = str[5]; + if (c == 'r') { + c = str[6]; + if (c == 'e') { + c = str[7]; + if (c == '-') { + c = str[8]; + if (c == 'r') { + c = str[9]; + if (c == 'u') { + c = str[10]; + if (c == 'n') { + c = str[11]; + if (c == 's') { + c = str[12]; + if (!c) return NEW_SRV_ACTION_COMPARE_RUNS; + return 0; + } return 0; } return 0; @@ -892,36 +935,44 @@ int ns_match_action(const unsigned char *str) } return 0; } - return 0; + } else { } - } else { + return 0; } return 0; } - return 0; + } else { } } else { - if (c == 'o') { + if (c == 'p') { c = str[3]; - if (c == 'k') { + if (c == 'y') { c = str[4]; - if (c == 'i') { + if (c == '-') { c = str[5]; - if (c == 'e') { + if (c == 'u') { c = str[6]; - if (c == '-') { + if (c == 's') { c = str[7]; - if (c == 'l') { + if (c == 'e') { c = str[8]; - if (c == 'o') { + if (c == 'r') { c = str[9]; - if (c == 'g') { + if (c == '-') { c = str[10]; if (c == 'i') { c = str[11]; if (c == 'n') { c = str[12]; - if (!c) return NEW_SRV_ACTION_COOKIE_LOGIN; + if (c == 'f') { + c = str[13]; + if (c == 'o') { + c = str[14]; + if (!c) return NEW_SRV_ACTION_COPY_USER_INFO; + return 0; + } + return 0; + } return 0; } return 0; @@ -2107,6 +2158,53 @@ int ns_match_action(const unsigned char *str) return 0; } return 0; + } else if (c < 'u') { + if (c == 'e') { + c = str[9]; + if (c == 'g') { + c = str[10]; + if (c == 'i') { + c = str[11]; + if (c == 's') { + c = str[12]; + if (c == 't') { + c = str[13]; + if (c == 'r') { + c = str[14]; + if (c == 'a') { + c = str[15]; + if (c == 't') { + c = str[16]; + if (c == 'i') { + c = str[17]; + if (c == 'o') { + c = str[18]; + if (c == 'n') { + c = str[19]; + if (!c) return NEW_SRV_ACTION_CHANGE_REGISTRATION; + return 0; + } + return 0; + } + return 0; + } + return 0; + } + return 0; + } + return 0; + } + return 0; + } + return 0; + } + return 0; + } + return 0; + } + return 0; + } + } else { } return 0; } diff --git a/gen/prepare_meta.c b/gen/prepare_meta.c index c44d8c181..03a008d82 100644 --- a/gen/prepare_meta.c +++ b/gen/prepare_meta.c @@ -1378,7 +1378,7 @@ static struct meta_info_item meta_info_section_problem_data_data[] = [CNTSPROB_unhandled_vars] = { CNTSPROB_unhandled_vars, 's', XSIZE(struct section_problem_data, unhandled_vars), "unhandled_vars", XOFFSET(struct section_problem_data, unhandled_vars) }, [CNTSPROB_score_view] = { CNTSPROB_score_view, 'x', XSIZE(struct section_problem_data, score_view), "score_view", XOFFSET(struct section_problem_data, score_view) }, [CNTSPROB_score_view_score] = { CNTSPROB_score_view_score, '?', XSIZE(struct section_problem_data, score_view_score), NULL, XOFFSET(struct section_problem_data, score_view_score) }, - [CNTSPROB_score_view_text] = { CNTSPROB_score_view_text, 'x', XSIZE(struct section_problem_data, score_view_text), "score_view_text", XOFFSET(struct section_problem_data, score_view_text) }, + [CNTSPROB_score_view_text] = { CNTSPROB_score_view_text, 'x', XSIZE(struct section_problem_data, score_view_text), NULL, XOFFSET(struct section_problem_data, score_view_text) }, [CNTSPROB_xml_file_path] = { CNTSPROB_xml_file_path, 's', XSIZE(struct section_problem_data, xml_file_path), NULL, XOFFSET(struct section_problem_data, xml_file_path) }, [CNTSPROB_var_xml_file_paths] = { CNTSPROB_var_xml_file_paths, 'x', XSIZE(struct section_problem_data, var_xml_file_paths), NULL, XOFFSET(struct section_problem_data, var_xml_file_paths) }, }; @@ -1800,7 +1800,7 @@ void cntsprob_copy(struct section_problem_data *dst, const struct section_proble } dst->score_view = (typeof(dst->score_view)) sarray_copy((char**) src->score_view); // private score_view_score - dst->score_view_text = (typeof(dst->score_view_text)) sarray_copy((char**) src->score_view_text); + // private score_view_text // private xml_file_path // private var_xml_file_paths // hidden xml @@ -1929,7 +1929,7 @@ void cntsprob_free(struct section_problem_data *ptr) free(ptr->unhandled_vars); sarray_free((char**) ptr->score_view); // private score_view_score - sarray_free((char**) ptr->score_view_text); + // private score_view_text // private xml_file_path // private var_xml_file_paths // hidden xml diff --git a/include/ejudge/json_serializers.h b/include/ejudge/json_serializers.h index 5e80c3843..b1dbcbb61 100644 --- a/include/ejudge/json_serializers.h +++ b/include/ejudge/json_serializers.h @@ -34,6 +34,12 @@ json_serialize_run( struct userlist_user; struct userlist_user_info; struct userlist_contest; + +struct cJSON * +json_serialize_userlist_contest( + int user_id, + const struct userlist_contest *uc); + struct cJSON * json_serialize_userlist_user( const struct userlist_user *u, diff --git a/include/ejudge/new_server_proto.h b/include/ejudge/new_server_proto.h index 3e1a31f9a..2140f4969 100644 --- a/include/ejudge/new_server_proto.h +++ b/include/ejudge/new_server_proto.h @@ -385,6 +385,8 @@ enum NEW_SRV_ACTION_JOB_STATUS_PAGE, NEW_SRV_ACTION_DOWNLOAD_JOB_RESULT, NEW_SRV_ACTION_GET_USER, + NEW_SRV_ACTION_COPY_USER_INFO, + NEW_SRV_ACTION_CHANGE_REGISTRATION, NEW_SRV_ACTION_LAST, }; @@ -572,6 +574,7 @@ enum NEW_SRV_ERR_INV_USERPROB_ID, NEW_SRV_ERR_INV_EXT_USER, NEW_SRV_ERR_INV_NOTIFY, + NEW_SRV_ERR_ALREADY_EXISTS, NEW_SRV_ERR_LAST, }; diff --git a/include/ejudge/prepare.h b/include/ejudge/prepare.h index 8560e1d5f..5860ec0cb 100644 --- a/include/ejudge/prepare.h +++ b/include/ejudge/prepare.h @@ -1317,7 +1317,7 @@ struct section_problem_data /** external score view */ char **score_view; int *score_view_score META_ATTRIB((meta_private)); - char **score_view_text; + char **score_view_text META_ATTRIB((meta_private)); /** full path to xml_file */ unsigned char *xml_file_path META_ATTRIB((meta_private)); diff --git a/l10n/ejudge.kk_KZ.UTF-8.po b/l10n/ejudge.kk_KZ.UTF-8.po index 8264463f6..67b4863cd 100644 --- a/l10n/ejudge.kk_KZ.UTF-8.po +++ b/l10n/ejudge.kk_KZ.UTF-8.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: 3.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-02-04 16:05+0300\n" +"POT-Creation-Date: 2024-02-21 22:06+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: Kazakh \n" @@ -426,6 +426,9 @@ msgstr "" msgid "Allow viewing the judge comment of the valuer" msgstr "" +msgid "Already exists" +msgstr "" + #, c-format msgid "An e-mail messages is sent to the address %s." msgstr "" diff --git a/l10n/ejudge.ru_RU.UTF-8.po b/l10n/ejudge.ru_RU.UTF-8.po index acd28d260..bf78640f7 100644 --- a/l10n/ejudge.ru_RU.UTF-8.po +++ b/l10n/ejudge.ru_RU.UTF-8.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: ejudge 3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-02-04 16:05+0300\n" +"POT-Creation-Date: 2024-02-21 22:06+0300\n" "PO-Revision-Date: 2022-01-12 13:17+0300\n" "Last-Translator: Alexander Chernov \n" "Language-Team: Russian \n" @@ -473,6 +473,9 @@ msgid "Allow viewing the judge comment of the valuer" msgstr "" "Разрешить просмотр комментария для судей, полученного от программы оценивания" +msgid "Already exists" +msgstr "Уже существует" + #, c-format msgid "An e-mail messages is sent to the address %s." msgstr "" diff --git a/l10n/ejudge.uk_UA.UTF-8.po b/l10n/ejudge.uk_UA.UTF-8.po index 1cbda5ddc..f3aeaff2a 100644 --- a/l10n/ejudge.uk_UA.UTF-8.po +++ b/l10n/ejudge.uk_UA.UTF-8.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: 3.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-02-04 16:05+0300\n" +"POT-Creation-Date: 2024-02-21 22:06+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: Ukrainian \n" @@ -426,6 +426,9 @@ msgstr "" msgid "Allow viewing the judge comment of the valuer" msgstr "" +msgid "Already exists" +msgstr "" + #, c-format msgid "An e-mail messages is sent to the address %s." msgstr "" diff --git a/lib/http_request.c b/lib/http_request.c index 01e03f9a1..832e8fb2c 100644 --- a/lib/http_request.c +++ b/lib/http_request.c @@ -1,6 +1,6 @@ /* -*- c -*- */ -/* Copyright (C) 2014-2021 Alexander Chernov */ +/* Copyright (C) 2014-2024 Alexander Chernov */ /* * This program is free software; you can redistribute it and/or modify @@ -315,6 +315,14 @@ hr_cgi_param_jsbool_opt( if (p_val) *p_val = 1; return 1; } + if (!strcasecmp(s, "no")) { + if (p_val) *p_val = 0; + return 1; + } + if (!strcasecmp(s, "yes")) { + if (p_val) *p_val = 1; + return 1; + } errno = 0; v = strtol(s, &eptr, 10); if (errno || *eptr) { diff --git a/lib/new_server_at.c b/lib/new_server_at.c index 3ac913879..f8834aa7f 100644 --- a/lib/new_server_at.c +++ b/lib/new_server_at.c @@ -341,4 +341,6 @@ const unsigned char * const ns_symbolic_action_table[NEW_SRV_ACTION_LAST] = [NEW_SRV_ACTION_JOB_STATUS_PAGE] = "job-status-page", [NEW_SRV_ACTION_DOWNLOAD_JOB_RESULT] = "download-job-result", [NEW_SRV_ACTION_GET_USER] = "get-user", + [NEW_SRV_ACTION_COPY_USER_INFO] = "copy-user-info", + [NEW_SRV_ACTION_CHANGE_REGISTRATION] = "change-registration", }; diff --git a/lib/new_server_html.c b/lib/new_server_html.c index a088efdc4..3a8d4c1f5 100644 --- a/lib/new_server_html.c +++ b/lib/new_server_html.c @@ -6802,6 +6802,7 @@ priv_download_runs( } cleanup: + xfree(mask); return retval; invalid_param: @@ -9678,6 +9679,451 @@ done:; } } +static void +priv_copy_user_info( + FILE *fout, + struct http_request_info *phr, + const struct contest_desc *cnts, + struct contest_extra *extra) +{ + int ok = 0; + int err_num = NEW_SRV_ERR_INV_PARAM; + const unsigned char *err_msg = NULL; + cJSON *jr = cJSON_CreateObject(); + int http_status = 400; + int other_user_id = 0; + const unsigned char *other_user_login = NULL; + int r; + unsigned char *login_str = NULL; + const struct contest_desc *from_cnts = NULL; + const struct contest_desc *to_cnts = NULL; + int from_contest_id = 0; + int to_contest_id = 0; + opcap_t caps, req_caps; + + if (hr_cgi_param_int_opt(phr, "other_user_id", &other_user_id, 0) < 0) + goto done; + if (hr_cgi_param(phr, "other_user_login", &other_user_login) < 0) + goto done; + if (other_user_id <= 0 && (!other_user_login || !other_user_login[0])) + goto done; + if (other_user_id > 0 && other_user_login && other_user_login[0]) + goto done; + + if (ns_open_ul_connection(phr->fw_state) < 0) { + err("priv_copy_user_info: failed to open userlist connection"); + goto done; + } + + if (other_user_login && other_user_login[0]) { + r = userlist_clnt_lookup_user(ul_conn, other_user_login, 0, &other_user_id, NULL); + if (r < 0 && r != -ULS_ERR_INVALID_LOGIN) { + err("priv_copy_user_info: userlist server error %d", r); + http_status = 500; + err_num = NEW_SRV_ERR_INTERNAL; + goto done; + } + if (r < 0) { + err_num = NEW_SRV_ERR_INV_USER_ID; + http_status = 404; + goto done; + } + } else { + r = userlist_clnt_lookup_user_id(ul_conn, other_user_id, 0, &login_str, NULL); + if (r < 0 && r != -ULS_ERR_BAD_UID) { + err("priv_copy_user_info: userlist server error %d", r); + http_status = 500; + err_num = NEW_SRV_ERR_INTERNAL; + goto done; + } + if (r < 0) { + err_num = NEW_SRV_ERR_INV_USER_ID; + http_status = 404; + goto done; + } + other_user_login = login_str; + } + + // other_user_id/other_user_login - valid + + if (hr_cgi_param_int_opt(phr, "from_contest_id", &from_contest_id, 0) < 0 || from_contest_id < 0) + goto done; + if (!from_contest_id) from_contest_id = phr->contest_id; + if (hr_cgi_param_int_opt(phr, "to_contest_id", &to_contest_id, 0) < 0 || to_contest_id < 0) + goto done; + if (!to_contest_id) to_contest_id = phr->contest_id; + if (from_contest_id == to_contest_id) + goto done; + if (contests_get(from_contest_id, &from_cnts) < 0 || !from_cnts) + goto done; + if (contests_get(to_contest_id, &to_cnts) < 0 || !to_cnts) + goto done; + + req_caps = (1ULL << OPCAP_LIST_USERS) | (1ULL << OPCAP_GET_USER); + if (opcaps_find(&from_cnts->capabilities, phr->login, &caps) < 0 + || (caps & req_caps) != req_caps) { + err("priv_copy_user_info: insufficient permissions for user '%s' and contest %d", phr->login, from_contest_id); + http_status = 403; + err_num = NEW_SRV_ERR_PERMISSION_DENIED; + goto done; + } + req_caps = (1ULL << OPCAP_LIST_USERS) | (1ULL << OPCAP_GET_USER); + if (opcaps_find(&to_cnts->capabilities, other_user_login, &caps) >= 0) { + // the target user is a privileged user + req_caps |= (1ULL << OPCAP_PRIV_EDIT_USER) | (1ULL << OPCAP_PRIV_CREATE_REG) | (1ULL << OPCAP_PRIV_EDIT_REG); + if (to_cnts->disable_team_password <= 0) { + req_caps |= (1ULL << OPCAP_PRIV_EDIT_PASSWD); + } + } else { + // the target user is an unprivileged user + req_caps |= (1ULL << OPCAP_EDIT_USER) | (1ULL << OPCAP_CREATE_REG) | (1ULL << OPCAP_EDIT_REG); + if (to_cnts->disable_team_password <= 0) { + req_caps |= (1ULL << OPCAP_EDIT_PASSWD); + } + } + if (opcaps_find(&to_cnts->capabilities, phr->login, &caps) < 0 + || (caps & req_caps) != req_caps) { + err("priv_copy_user_info: insufficient permissions for user '%s' and contest %d", phr->login, to_contest_id); + http_status = 403; + err_num = NEW_SRV_ERR_PERMISSION_DENIED; + goto done; + } + + r = userlist_clnt_copy_user_info(ul_conn, ULS_COPY_ALL, other_user_id, from_contest_id, to_contest_id); + if (r < 0) { + err("priv_copy_user_info: userlist server error %d", r); + http_status = 500; + err_num = NEW_SRV_ERR_INTERNAL; + goto done; + } + + cJSON_AddTrueToObject(jr, "result"); + ok = 1; + err_num = 0; + http_status = 200; + +done:; + phr->json_reply = 1; + phr->status_code = http_status; + emit_json_result(fout, phr, ok, err_num, 0, err_msg, jr); + if (jr) { + cJSON_Delete(jr); + } + xfree(login_str); +} + +static void +priv_change_registration( + FILE *fout, + struct http_request_info *phr, + const struct contest_desc *cnts, + struct contest_extra *extra) +{ + int ok = 0; + int err_num = NEW_SRV_ERR_INV_PARAM; + const unsigned char *err_msg = NULL; + cJSON *jr = cJSON_CreateObject(); + int http_status = 400; + int other_user_id = 0; + const unsigned char *other_user_login = NULL; + int r; + unsigned char *login_str = NULL; + const unsigned char *op = NULL; + opcap_t caps = 0, req_caps = 0; + unsigned char *xml_text = NULL; + struct userlist_user *u = NULL; + const struct userlist_contest *uc = NULL; + + if (hr_cgi_param_int_opt(phr, "other_user_id", &other_user_id, 0) < 0) + goto done; + if (hr_cgi_param(phr, "other_user_login", &other_user_login) < 0) + goto done; + if (other_user_id <= 0 && (!other_user_login || !other_user_login[0])) + goto done; + if (other_user_id > 0 && other_user_login && other_user_login[0]) + goto done; + + if (ns_open_ul_connection(phr->fw_state) < 0) { + err("%s: failed to open userlist connection", __PRETTY_FUNCTION__); + goto done; + } + + if (other_user_login && other_user_login[0]) { + r = userlist_clnt_lookup_user(ul_conn, other_user_login, 0, &other_user_id, NULL); + if (r < 0 && r != -ULS_ERR_INVALID_LOGIN) { + goto userlist_error; + } + if (r < 0) { + err_num = NEW_SRV_ERR_INV_USER_ID; + http_status = 404; + goto done; + } + } else { + r = userlist_clnt_lookup_user_id(ul_conn, other_user_id, 0, &login_str, NULL); + if (r < 0 && r != -ULS_ERR_BAD_UID) { + goto userlist_error; + } + if (r < 0) { + err_num = NEW_SRV_ERR_INV_USER_ID; + http_status = 404; + goto done; + } + other_user_login = login_str; + } + // other_user_id/other_user_login - valid + + if (phr->contest_id <= 0 || contests_get(phr->contest_id, &cnts) < 0 || !cnts) { + goto done; + } + // contest_id - valid + + if (hr_cgi_param(phr, "op", &op) <= 0 || !op) { + goto done; + } + // delete, upsert, update, insert + if (strcmp(op, "delete") && strcmp(op, "insert") && strcmp(op, "upsert") && strcmp(op, "update")) { + err("%s: invalid operation", __PRETTY_FUNCTION__); + goto done; + } + + if (!strcmp(op, "delete")) { + if (opcaps_find(&cnts->capabilities, other_user_login, &caps) >= 0) { + req_caps = (1ULL << OPCAP_PRIV_DELETE_REG); + } else { + req_caps = (1ULL << OPCAP_DELETE_REG); + } + if (opcaps_find(&cnts->capabilities, phr->login, &caps) < 0 + || (caps & req_caps) != req_caps) { + err("%s: insufficient '%s' for user '%s' and contest %d", __PRETTY_FUNCTION__, op, phr->login, phr->contest_id); + http_status = 403; + err_num = NEW_SRV_ERR_PERMISSION_DENIED; + goto done; + } + r = userlist_clnt_change_registration(ul_conn, other_user_id, phr->contest_id, -2, 0, 0); + if (r < 0) { + goto userlist_error; + } + + cJSON_AddNullToObject(jr, "result"); + ok = 1; + err_num = 0; + http_status = 200; + goto done; + } + + // common parse registration data + const unsigned char *s = NULL; + int status = -1; + if (hr_cgi_param(phr, "status", &s) > 0 && s) { + if (!strcasecmp(s, "ok")) { + status = USERLIST_REG_OK; + } else if (!strcasecmp(s, "pending")) { + status = USERLIST_REG_PENDING; + } else if (!strcasecmp(s, "rejected")) { + status = USERLIST_REG_REJECTED; + } else { + char *eptr = NULL; + errno = 0; + long val = strtol(s, &eptr, 10); + if (errno || *eptr || s == (const unsigned char*) eptr || val < 0 || val > 2) { + goto done; + } + status = (int) val; + } + } + int is_invisible = -1; + if (hr_cgi_param_jsbool_opt(phr, "is_invisible", &is_invisible, -1) < 0) { + goto done; + } + int is_banned = -1; + if (hr_cgi_param_jsbool_opt(phr, "is_banned", &is_banned, -1) < 0) { + goto done; + } + int is_locked = -1; + if (hr_cgi_param_jsbool_opt(phr, "is_locked", &is_locked, -1) < 0) { + goto done; + } + int is_incomplete = -1; + if (hr_cgi_param_jsbool_opt(phr, "is_incomplete", &is_incomplete, -1) < 0) { + goto done; + } + int is_disqualified = -1; + if (hr_cgi_param_jsbool_opt(phr, "is_disqualified", &is_disqualified, -1) < 0) { + goto done; + } + int is_privileged = -1; + if (hr_cgi_param_jsbool_opt(phr, "is_privileged", &is_privileged, -1) < 0) { + goto done; + } + int is_reg_readonly = -1; + if (hr_cgi_param_jsbool_opt(phr, "is_reg_readonly", &is_reg_readonly, -1) < 0) { + goto done; + } + int ignore = 0; + if (hr_cgi_param_jsbool_opt(phr, "ignore", &ignore, 0) < 0) { + goto done; + } + int clear_name = 0; + if (hr_cgi_param_jsbool_opt(phr, "clear_name", &clear_name, 0) < 0) { + goto done; + } + const unsigned char *name = NULL; + if (hr_cgi_param(phr, "name", &name) < 0) { + goto done; + } + int flags_to_set = 0; + if (is_invisible > 0) flags_to_set |= USERLIST_UC_INVISIBLE; + if (is_banned > 0) flags_to_set |= USERLIST_UC_BANNED; + if (is_locked > 0) flags_to_set |= USERLIST_UC_LOCKED; + if (is_incomplete > 0) flags_to_set |= USERLIST_UC_INCOMPLETE; + if (is_disqualified > 0) flags_to_set |= USERLIST_UC_DISQUALIFIED; + if (is_privileged > 0) flags_to_set |= USERLIST_UC_PRIVILEGED; + if (is_reg_readonly > 0) flags_to_set |= USERLIST_UC_REG_READONLY; + int flags_to_clear = 0; + if (!is_invisible) flags_to_clear |= USERLIST_UC_INVISIBLE; + if (!is_banned) flags_to_clear |= USERLIST_UC_BANNED; + if (!is_locked) flags_to_clear |= USERLIST_UC_LOCKED; + if (!is_incomplete) flags_to_clear |= USERLIST_UC_INCOMPLETE; + if (!is_disqualified) flags_to_clear |= USERLIST_UC_DISQUALIFIED; + if (!is_privileged) flags_to_clear |= USERLIST_UC_PRIVILEGED; + if (!is_reg_readonly) flags_to_clear |= USERLIST_UC_REG_READONLY; + + r = userlist_clnt_get_info(ul_conn, ULS_PRIV_GET_USER_INFO, other_user_id, phr->contest_id, &xml_text); + if (r < 0 && r != -ULS_ERR_BAD_UID) { + goto userlist_error; + } + if (r < 0) { + err_num = NEW_SRV_ERR_INV_USER_ID; + http_status = 404; + goto done; + } + u = userlist_parse_user_str(xml_text); + if (!u) { + err("%s: XML parse error", __PRETTY_FUNCTION__); + http_status = 500; + err_num = NEW_SRV_ERR_INTERNAL; + goto done; + } + uc = userlist_get_user_contest(u, phr->contest_id); + + if (!strcmp(op, "insert")) { + if (uc && ignore > 0) { + cJSON *jrc = json_serialize_userlist_contest(other_user_id, uc); + cJSON_AddItemToObject(jr, "result", jrc); + ok = 1; + err_num = 0; + http_status = 200; + goto done; + } + if (uc) { + err("%s: user %d already registered to contest %d", __PRETTY_FUNCTION__, other_user_id, phr->contest_id); + http_status = 409; + err_num = NEW_SRV_ERR_ALREADY_PRINTED; + goto done; + } + } + if (!strcmp(op, "insert") || !strcmp(op, "upsert")) { + if (!uc) { + r = userlist_clnt_register_contest(ul_conn, + ULS_PRIV_REGISTER_CONTEST, + other_user_id, phr->contest_id, 0, 0); + if (r < 0) { + goto userlist_error; + } + } + } + if (!strcmp(op, "update")) { + if (!uc && ignore > 0) { + cJSON_AddNullToObject(jr, "result"); + ok = 1; + err_num = 0; + http_status = 200; + goto done; + } + if (!uc) { + err("%s: user %d is not registered to contest %d", __PRETTY_FUNCTION__, other_user_id, phr->contest_id); + http_status = 404; + err_num = NEW_SRV_ERR_INV_CONTEST_ID; + goto done; + } + } + + // status: -1 - no change + // flags_cmd: 1 - set, 2 - clear + if (status >= 0 && (r = userlist_clnt_change_registration(ul_conn, other_user_id, phr->contest_id, status, 0, 0)) < 0) { + goto userlist_error; + } + if (flags_to_set > 0 && (r = userlist_clnt_change_registration(ul_conn, other_user_id, phr->contest_id, -1, 1, flags_to_set)) < 0) { + goto userlist_error; + } + if (flags_to_set > 0 && (r = userlist_clnt_change_registration(ul_conn, other_user_id, phr->contest_id, -1, 2, flags_to_clear)) < 0) { + goto userlist_error; + } + + if (clear_name > 0) { + if ((r = userlist_clnt_delete_field(ul_conn, ULS_DELETE_FIELD, other_user_id, phr->contest_id, 0, USERLIST_NC_NAME)) < 0) { + goto userlist_error; + } + } else if (name) { + if ((r = userlist_clnt_edit_field(ul_conn, ULS_EDIT_FIELD, + other_user_id, phr->contest_id, 0, + USERLIST_NC_NAME, name)) < 0) { + goto userlist_error; + } + } + + free(xml_text); xml_text = NULL; + if (u) { + userlist_free(&u->b); + } + u = NULL; + + r = userlist_clnt_get_info(ul_conn, ULS_PRIV_GET_USER_INFO, other_user_id, phr->contest_id, &xml_text); + if (r < 0) { + goto userlist_error; + } + u = userlist_parse_user_str(xml_text); + if (!u) { + err("%s: XML parse error", __PRETTY_FUNCTION__); + http_status = 500; + err_num = NEW_SRV_ERR_INTERNAL; + goto done; + } + uc = userlist_get_user_contest(u, phr->contest_id); + if (!uc) { + err("%s: registration not found", __PRETTY_FUNCTION__); + http_status = 500; + err_num = NEW_SRV_ERR_INTERNAL; + goto done; + } + + cJSON *jrc = json_serialize_userlist_contest(other_user_id, uc); + cJSON_AddItemToObject(jr, "result", jrc); + ok = 1; + err_num = 0; + http_status = 200; + +done:; + phr->json_reply = 1; + phr->status_code = http_status; + emit_json_result(fout, phr, ok, err_num, 0, err_msg, jr); + if (jr) { + cJSON_Delete(jr); + } + xfree(login_str); + free(xml_text); + if (u) { + userlist_free(&u->b); + } + return; + +userlist_error:; + err("%s: userlist server error %d", __PRETTY_FUNCTION__, r); + http_status = 500; + err_num = NEW_SRV_ERR_INTERNAL; + goto done; +} + typedef PageInterface *(*external_action_handler_t)(void); typedef int (*new_action_handler_t)( @@ -9916,6 +10362,8 @@ static action_handler_t actions_table[NEW_SRV_ACTION_LAST] = [NEW_SRV_ACTION_CLEAR_SESSION_CACHE] = priv_generic_operation, [NEW_SRV_ACTION_DOWNLOAD_JOB_RESULT] = priv_download_job_result, [NEW_SRV_ACTION_GET_USER] = priv_get_user, + [NEW_SRV_ACTION_COPY_USER_INFO] = priv_copy_user_info, + [NEW_SRV_ACTION_CHANGE_REGISTRATION] = priv_change_registration, }; static const unsigned char * const external_priv_action_names[NEW_SRV_ACTION_LAST] = diff --git a/lib/new_server_html_2.c b/lib/new_server_html_2.c index 0d5aac8ab..73e6b36a3 100644 --- a/lib/new_server_html_2.c +++ b/lib/new_server_html_2.c @@ -3148,6 +3148,7 @@ adj_destroy_func(struct server_framework_job *sfj) xfree(adj->dirname); xfree(adj->dirpath); xfree(adj->b.title); + xfree(adj->problem_dir_prefix); if (adj->log_f) fclose(adj->log_f); free(adj->log_s); xfree(adj->runs); diff --git a/lib/new_server_proto.c b/lib/new_server_proto.c index 8fdf0d1f0..87aab7887 100644 --- a/lib/new_server_proto.c +++ b/lib/new_server_proto.c @@ -1,6 +1,6 @@ /* -*- mode: c -*- */ -/* Copyright (C) 2006-2023 Alexander Chernov */ +/* Copyright (C) 2006-2024 Alexander Chernov */ /* * This program is free software; you can redistribute it and/or modify @@ -192,6 +192,7 @@ static const unsigned char * const ns_error_messages[NEW_SRV_ERR_LAST]= [NEW_SRV_ERR_INV_USERPROB_ID] = __("Invalid ID"), [NEW_SRV_ERR_INV_EXT_USER] = __("Invalid external user"), [NEW_SRV_ERR_INV_NOTIFY] = __("Invalid notification"), + [NEW_SRV_ERR_ALREADY_EXISTS] = __("Already exists"), }; static const unsigned char * const ns_error_titles[NEW_SRV_ERR_LAST]= @@ -356,6 +357,7 @@ static const unsigned char * const ns_error_titles[NEW_SRV_ERR_LAST]= [NEW_SRV_ERR_INV_USERPROB_ID] = __("Invalid ID"), [NEW_SRV_ERR_INV_EXT_USER] = __("Invalid external user"), [NEW_SRV_ERR_INV_NOTIFY] = __("Invalid notification"), + [NEW_SRV_ERR_ALREADY_EXISTS] = __("Already exists"), }; static const unsigned char * const ns_error_symbols[NEW_SRV_ERR_LAST]= @@ -519,6 +521,7 @@ static const unsigned char * const ns_error_symbols[NEW_SRV_ERR_LAST]= [NEW_SRV_ERR_INV_USERPROB_ID] = "ERR_INV_USERPROB_ID", [NEW_SRV_ERR_INV_EXT_USER] = "ERR_INV_EXT_USER", [NEW_SRV_ERR_INV_NOTIFY] = "ERR_INV_NOTIFY", + [NEW_SRV_ERR_ALREADY_EXISTS] = "ERR_ALREADY_EXISTS", }; const unsigned char * diff --git a/lib/prepare.c b/lib/prepare.c index f477b08c9..6eaad089b 100644 --- a/lib/prepare.c +++ b/lib/prepare.c @@ -1275,6 +1275,7 @@ prepare_problem_free_func(struct generic_section_config *gp) free_deadline_penalties(p->dp_total, p->dp_infos); free_personal_deadlines(p->pd_total, p->pd_infos); xfree(p->score_view_score); + xfree(p->score_view_text); xfree(p->xml_file_path); if (p->variant_num > 0 && p->xml.a) {