From 0ec99d8ba1bcd143d70c6cdeb662486b1e0e8a21 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sun, 21 Jun 2020 17:03:32 +0900 Subject: [PATCH] =?UTF-8?q?=EC=84=9C=EB=B2=84=20=EC=BB=A4=EC=8A=A4?= =?UTF-8?q?=ED=84=B0=EB=A7=88=EC=9D=B4=EC=A7=95=20=EA=B0=80=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- f_install/j_setup_db.php | 618 +++++---- f_install/templates/ServConfig.orig.php | 129 +- hwe/j_autoreset.php | 200 +-- hwe/j_install.php | 416 +++--- hwe/j_install_db.php | 204 +-- hwe/sammo/ResetHelper.php | 714 +++++----- i_entrance/j_get_user_info.php | 180 +-- i_entrance/j_get_userlist.php | 114 +- i_entrance/j_icon_change.php | 212 +-- i_entrance/j_icon_delete.php | 110 +- i_entrance/j_server_change_status.php | 234 ++-- i_entrance/j_server_get_admin_status.php | 178 +-- i_entrance/j_server_get_status.php | 50 +- j_updateServer.php | 480 +++---- src/sammo/AppConf.php | 148 +- src/sammo/Util.php | 1620 +++++++++++----------- 16 files changed, 2815 insertions(+), 2792 deletions(-) diff --git a/f_install/j_setup_db.php b/f_install/j_setup_db.php index 5990f0ae..fca276b2 100644 --- a/f_install/j_setup_db.php +++ b/f_install/j_setup_db.php @@ -1,305 +1,313 @@ - false, - 'reason' => '입력 값이 올바르지 않습니다' - ]); -} - -if (!filter_var($servHost, FILTER_VALIDATE_URL)) { - Json::die([ - 'result' => false, - 'reason' => '접속 경로가 올바르지 않습니다.' - ]); -} - -if (file_exists(ROOT . '/d_setting/RootDB.php') && is_dir(ROOT . '/d_setting/RootDB.php')) { - Json::die([ - 'result' => false, - 'reason' => 'd_setting/RootDB.php 가 디렉토리입니다' - ]); -} - -if (class_exists('\\sammo\\RootDB')) { - Json::die([ - 'result' => false, - 'reason' => '이미 RootDB.php 파일이 있습니다' - ]); -} - -//파일 권한 검사 -if (file_exists(AppConf::getUserIconPathFS()) && !is_dir(AppConf::getUserIconPathFS())) { - Json::die([ - 'result' => false, - 'reason' => AppConf::$userIconPath . ' 이 디렉토리가 아닙니다' - ]); -} - -if (file_exists(ROOT . '/d_log') && !is_dir(ROOT . '/d_log')) { - Json::die([ - 'result' => false, - 'reason' => 'd_log 가 디렉토리가 아닙니다' - ]); -} - -if (file_exists(ROOT . '/d_shared') && !is_dir(ROOT . '/d_shared')) { - Json::die([ - 'result' => false, - 'reason' => 'd_shared 가 디렉토리가 아닙니다' - ]); -} - -if (file_exists(ROOT . '/d_setting') && !is_dir(ROOT . '/d_setting')) { - Json::die([ - 'result' => false, - 'reason' => 'd_shared 가 디렉토리가 아닙니다' - ]); -} - -if ( - !file_exists(ROOT . '/d_log') - || !file_exists(ROOT . '/d_shared') - || !file_exists(ROOT . '/d_setting') - || !file_exists(AppConf::getUserIconPathFS()) -) { - if (!is_writable(ROOT)) { - Json::die([ - 'result' => false, - 'reason' => '하위 디렉토리 생성 권한이 없습니다' - ]); - } - - //기본 파일 생성 - if (!file_exists(AppConf::getUserIconPathFS())) { - mkdir(AppConf::getUserIconPathFS()); - } - - if (!file_exists(ROOT . '/d_log')) { - mkdir(ROOT . '/d_log'); - } - - if (!file_exists(ROOT . '/d_setting')) { - mkdir(ROOT . '/d_setting'); - } - - if (!file_exists(ROOT . '/d_shared')) { - mkdir(ROOT . '/d_shared'); - } -} - - -if (!is_writable(AppConf::getUserIconPathFS())) { - Json::die([ - 'result' => false, - 'reason' => AppConf::$userIconPath . ' 디렉토리의 쓰기 권한이 없습니다' - ]); -} - -if (!is_writable(ROOT . '/d_log')) { - Json::die([ - 'result' => false, - 'reason' => 'd_log 디렉토리의 쓰기 권한이 없습니다' - ]); -} - -if (!is_writable(ROOT . '/d_shared')) { - Json::die([ - 'result' => false, - 'reason' => 'd_shared 디렉토리의 쓰기 권한이 없습니다' - ]); -} - -if (!is_writable(ROOT . '/d_setting')) { - Json::die([ - 'result' => false, - 'reason' => 'd_setting 디렉토리의 쓰기 권한이 없습니다.' - ]); -} - - - -if (!file_exists(ROOT . '/d_log/.htaccess')) { - @file_put_contents(ROOT . '/d_log/.htaccess', 'Deny from all'); -} - -if (!file_exists(ROOT . '/d_setting/.htaccess')) { - @file_put_contents(ROOT . '/d_setting/.htaccess', 'Deny from all'); -} - -//DB 접근 권한 검사 - -$rootDB = new \MeekroDB($host, $username, $password, $dbName, $port, 'utf8mb4'); -$rootDB->connect_options[MYSQLI_OPT_INT_AND_FLOAT_NATIVE] = true; - -$rootDB->throw_exception_on_nonsql_error = false; -$rootDB->nonsql_error_handler = function ($params) { - Json::die([ - 'result' => false, - 'reason' => 'DB 접속에 실패했습니다.' - ]); -}; - -$rootDB->error_handler = function ($params) { - Json::die([ - 'result' => false, - 'reason' => 'SQL을 제대로 실행하지 못했습니다. DB상태를 확인해 주세요.' - ]); -}; - -$mysqli_obj = $rootDB->get(); //로그인에 실패할 경우 자동으로 dbConnFail()이 실행됨. - -if ($mysqli_obj->multi_query(file_get_contents(__DIR__ . '/sql/common_schema.sql'))) { - while (true) { - if (!$mysqli_obj->more_results()) { - break; - } - if (!$mysqli_obj->next_result()) { - break; - } - } -} - -$rootDB->insert('system', array( - 'REG' => 'N', - 'LOGIN' => 'N', - 'CRT_DATE' => TimeUtil::now(), - 'MDF_DATE' => TimeUtil::now() -)); - -$globalSalt = bin2hex(random_bytes(16)); - -'@phan-var-force string $servHost'; -'@phan-var-force string $sharedIconPath'; -'@phan-var-force string $gameImagePath'; - -$sharedIconPath = WebUtil::resolveRelativePath($sharedIconPath, $servHost); -$gameImagePath = WebUtil::resolveRelativePath($gameImagePath, $servHost); -$imageRequestPath = WebUtil::resolveRelativePath($gameImagePath . '/../hook/git_pull.php', $servHost); -$imageKeyInstallPath = WebUtil::resolveRelativePath($gameImagePath . '/../hook/InstallKey.php', $servHost); - -$result = Util::generateFileUsingSimpleTemplate( - __DIR__ . '/templates/ServConfig.orig.php', - ROOT . '/d_setting/ServConfig.php', - [ - 'serverBasePath' => $servHost, - 'sharedIconPath' => $sharedIconPath, - 'gameImagePath' => $gameImagePath, - 'imageRequestPath' => $imageRequestPath, - 'imageRequestKey' => $imageRequestKey - ], - true -); - -if ($imageRequestKey) { - @file_get_contents($imageKeyInstallPath . '?key=' . $imageRequestKey); -} - -if ($result !== true) { - Json::die([ - 'result' => false, - 'reason' => $result - ]); -} - -$result = Util::generateFileUsingSimpleTemplate( - __DIR__ . '/templates/common_path.orig.js', - ROOT . '/d_shared/common_path.js', - [ - 'serverBasePath' => $servHost, - 'sharedIconPath' => $sharedIconPath, - 'gameImagePath' => $gameImagePath - ], - true -); - -$result = Util::generateFileUsingSimpleTemplate( - __DIR__ . '/templates/menu.orig.json', - ROOT . '/d_shared/menu.json', - [], - true -); - -if ($result !== true) { - Json::die([ - 'result' => false, - 'reason' => $result - ]); -} - - -$result = Util::generateFileUsingSimpleTemplate( - __DIR__ . '/templates/common.orig.css', - ROOT . '/d_shared/common.css', - [ - 'serverBasePath' => $servHost, - 'sharedIconPath' => $sharedIconPath, - 'gameImagePath' => $gameImagePath - ], - true -); - -if ($result !== true) { - Json::die([ - 'result' => false, - 'reason' => $result - ]); -} - - -$result = Util::generateFileUsingSimpleTemplate( - __DIR__ . '/templates/RootDB.orig.php', - ROOT . '/d_setting/RootDB.php', - [ - 'host' => $host, - 'user' => $username, - 'password' => $password, - 'dbName' => $dbName, - 'port' => $port, - 'globalSalt' => $globalSalt, - ] -); - -$kakaoRedirectURI = WebUtil::resolveRelativePath('oauth_kakao/oauth.php', $servHost . '/'); - -Util::generateFileUsingSimpleTemplate( - __DIR__ . '/templates/KakaoKey.orig.php', - ROOT . '/d_setting/KakaoKey.php', - [ - 'REST_API_KEY' => $kakaoRESTKey, - 'ADMIN_KEY' => $kakaoAdminKey, - 'REDIRECT_URI' => $kakaoRedirectURI - ], - true -); - -if ($result !== true) { - Json::die([ - 'result' => false, - 'reason' => $result - ]); -} - -Json::die([ - 'result' => true, - 'reason' => 'success', - 'globalSalt' => $globalSalt -]); + false, + 'reason' => '입력 값이 올바르지 않습니다' + ]); +} + +if (!filter_var($servHost, FILTER_VALIDATE_URL)) { + Json::die([ + 'result' => false, + 'reason' => '접속 경로가 올바르지 않습니다.' + ]); +} + +if (file_exists(ROOT . '/d_setting/RootDB.php') && is_dir(ROOT . '/d_setting/RootDB.php')) { + Json::die([ + 'result' => false, + 'reason' => 'd_setting/RootDB.php 가 디렉토리입니다' + ]); +} + +if (class_exists('\\sammo\\RootDB')) { + Json::die([ + 'result' => false, + 'reason' => '이미 RootDB.php 파일이 있습니다' + ]); +} + +//파일 권한 검사 +if (file_exists(AppConf::getUserIconPathFS()) && !is_dir(AppConf::getUserIconPathFS())) { + Json::die([ + 'result' => false, + 'reason' => AppConf::$userIconPath . ' 이 디렉토리가 아닙니다' + ]); +} + +if (file_exists(ROOT . '/d_log') && !is_dir(ROOT . '/d_log')) { + Json::die([ + 'result' => false, + 'reason' => 'd_log 가 디렉토리가 아닙니다' + ]); +} + +if (file_exists(ROOT . '/d_shared') && !is_dir(ROOT . '/d_shared')) { + Json::die([ + 'result' => false, + 'reason' => 'd_shared 가 디렉토리가 아닙니다' + ]); +} + +if (file_exists(ROOT . '/d_setting') && !is_dir(ROOT . '/d_setting')) { + Json::die([ + 'result' => false, + 'reason' => 'd_shared 가 디렉토리가 아닙니다' + ]); +} + +if ( + !file_exists(ROOT . '/d_log') + || !file_exists(ROOT . '/d_shared') + || !file_exists(ROOT . '/d_setting') + || !file_exists(AppConf::getUserIconPathFS()) +) { + if (!is_writable(ROOT)) { + Json::die([ + 'result' => false, + 'reason' => '하위 디렉토리 생성 권한이 없습니다' + ]); + } + + //기본 파일 생성 + if (!file_exists(AppConf::getUserIconPathFS())) { + mkdir(AppConf::getUserIconPathFS()); + } + + if (!file_exists(ROOT . '/d_log')) { + mkdir(ROOT . '/d_log'); + } + + if (!file_exists(ROOT . '/d_setting')) { + mkdir(ROOT . '/d_setting'); + } + + if (!file_exists(ROOT . '/d_shared')) { + mkdir(ROOT . '/d_shared'); + } +} + + +if (!is_writable(AppConf::getUserIconPathFS())) { + Json::die([ + 'result' => false, + 'reason' => AppConf::$userIconPath . ' 디렉토리의 쓰기 권한이 없습니다' + ]); +} + +if (!is_writable(ROOT . '/d_log')) { + Json::die([ + 'result' => false, + 'reason' => 'd_log 디렉토리의 쓰기 권한이 없습니다' + ]); +} + +if (!is_writable(ROOT . '/d_shared')) { + Json::die([ + 'result' => false, + 'reason' => 'd_shared 디렉토리의 쓰기 권한이 없습니다' + ]); +} + +if (!is_writable(ROOT . '/d_setting')) { + Json::die([ + 'result' => false, + 'reason' => 'd_setting 디렉토리의 쓰기 권한이 없습니다.' + ]); +} + + + +if (!file_exists(ROOT . '/d_log/.htaccess')) { + @file_put_contents(ROOT . '/d_log/.htaccess', 'Deny from all'); +} + +if (!file_exists(ROOT . '/d_setting/.htaccess')) { + @file_put_contents(ROOT . '/d_setting/.htaccess', 'Deny from all'); +} + +//DB 접근 권한 검사 + +$rootDB = new \MeekroDB($host, $username, $password, $dbName, $port, 'utf8mb4'); +$rootDB->connect_options[MYSQLI_OPT_INT_AND_FLOAT_NATIVE] = true; + +$rootDB->throw_exception_on_nonsql_error = false; +$rootDB->nonsql_error_handler = function ($params) { + Json::die([ + 'result' => false, + 'reason' => 'DB 접속에 실패했습니다.' + ]); +}; + +$rootDB->error_handler = function ($params) { + Json::die([ + 'result' => false, + 'reason' => 'SQL을 제대로 실행하지 못했습니다. DB상태를 확인해 주세요.' + ]); +}; + +$mysqli_obj = $rootDB->get(); //로그인에 실패할 경우 자동으로 dbConnFail()이 실행됨. + +if ($mysqli_obj->multi_query(file_get_contents(__DIR__ . '/sql/common_schema.sql'))) { + while (true) { + if (!$mysqli_obj->more_results()) { + break; + } + if (!$mysqli_obj->next_result()) { + break; + } + } +} + +$rootDB->insert('system', array( + 'REG' => 'N', + 'LOGIN' => 'N', + 'CRT_DATE' => TimeUtil::now(), + 'MDF_DATE' => TimeUtil::now() +)); + +$globalSalt = bin2hex(random_bytes(16)); + +'@phan-var-force string $servHost'; +'@phan-var-force string $sharedIconPath'; +'@phan-var-force string $gameImagePath'; + +$sharedIconPath = WebUtil::resolveRelativePath($sharedIconPath, $servHost); +$gameImagePath = WebUtil::resolveRelativePath($gameImagePath, $servHost); +$imageRequestPath = WebUtil::resolveRelativePath($gameImagePath . '/../hook/git_pull.php', $servHost); +$imageKeyInstallPath = WebUtil::resolveRelativePath($gameImagePath . '/../hook/InstallKey.php', $servHost); + +$result = Util::generateFileUsingSimpleTemplate( + __DIR__ . '/templates/ServConfig.orig.php', + ROOT . '/d_setting/ServConfig.php', + [ + 'serverBasePath' => $servHost, + 'sharedIconPath' => $sharedIconPath, + 'gameImagePath' => $gameImagePath, + 'imageRequestPath' => $imageRequestPath, + 'imageRequestKey' => $imageRequestKey, + 'serverList' => [ + ['che', '체', 'white'], + ['kwe', '퀘', 'yellow'], + ['pwe', '풰', 'orange'], + ['twe', '퉤', 'magenta'], + ['nya', '냐', '#e67e22'], + ['pya', '퍄', '#9b59b6'] + ] + ], + true +); + +if ($imageRequestKey) { + @file_get_contents($imageKeyInstallPath . '?key=' . $imageRequestKey); +} + +if ($result !== true) { + Json::die([ + 'result' => false, + 'reason' => $result + ]); +} + +$result = Util::generateFileUsingSimpleTemplate( + __DIR__ . '/templates/common_path.orig.js', + ROOT . '/d_shared/common_path.js', + [ + 'serverBasePath' => $servHost, + 'sharedIconPath' => $sharedIconPath, + 'gameImagePath' => $gameImagePath + ], + true +); + +$result = Util::generateFileUsingSimpleTemplate( + __DIR__ . '/templates/menu.orig.json', + ROOT . '/d_shared/menu.json', + [], + true +); + +if ($result !== true) { + Json::die([ + 'result' => false, + 'reason' => $result + ]); +} + + +$result = Util::generateFileUsingSimpleTemplate( + __DIR__ . '/templates/common.orig.css', + ROOT . '/d_shared/common.css', + [ + 'serverBasePath' => $servHost, + 'sharedIconPath' => $sharedIconPath, + 'gameImagePath' => $gameImagePath + ], + true +); + +if ($result !== true) { + Json::die([ + 'result' => false, + 'reason' => $result + ]); +} + + +$result = Util::generateFileUsingSimpleTemplate( + __DIR__ . '/templates/RootDB.orig.php', + ROOT . '/d_setting/RootDB.php', + [ + 'host' => $host, + 'user' => $username, + 'password' => $password, + 'dbName' => $dbName, + 'port' => $port, + 'globalSalt' => $globalSalt, + ] +); + +$kakaoRedirectURI = WebUtil::resolveRelativePath('oauth_kakao/oauth.php', $servHost . '/'); + +Util::generateFileUsingSimpleTemplate( + __DIR__ . '/templates/KakaoKey.orig.php', + ROOT . '/d_setting/KakaoKey.php', + [ + 'REST_API_KEY' => $kakaoRESTKey, + 'ADMIN_KEY' => $kakaoAdminKey, + 'REDIRECT_URI' => $kakaoRedirectURI + ], + true +); + +if ($result !== true) { + Json::die([ + 'result' => false, + 'reason' => $result + ]); +} + +Json::die([ + 'result' => true, + 'reason' => 'success', + 'globalSalt' => $globalSalt +]); diff --git a/f_install/templates/ServConfig.orig.php b/f_install/templates/ServConfig.orig.php index 304f9bbb..3b4e3478 100644 --- a/f_install/templates/ServConfig.orig.php +++ b/f_install/templates/ServConfig.orig.php @@ -1,55 +1,74 @@ -queryFirstField("SHOW TABLES LIKE 'reserved_open'")) { - Json::die([ - 'result'=>true, - 'affected'=>0, - 'status'=>'no_reserved_table' - ]); -} - -$reserved = $db->queryFirstRow('SELECT `date`, options FROM reserved_open ORDER BY `date` ASC LIMIT 1'); - -if(!$reserved){ - Json::die([ - 'result'=>true, - 'affected'=>0, - 'status'=>'no_reserved' - ]); -} - -$reservedDate = new \DateTime($reserved['date']); -$now = new \DateTime(); - - -$status = 'not_yet'; - -list($isUnited, $lastTurn) = $gameStor->getValuesAsArray(['isunited', 'turntime']); -if($isUnited === null || $lastTurn === null){ - $isUnited = 2; - $lastTurn = '2000-01-01'; -} - -if($lastTurn !== null){ - $lastTurn = new \DateTime($lastTurn); -} - -if($lastTurn === null){ - //이미 리셋된 상태임 -} -else if(file_exists(__DIR__.'/.htaccess')){ - //일단 서버는 닫혀 있음 -} -else if( - $isUnited == 2 && - $now->getTimestamp() - $lastTurn->getTimestamp() > $reservedDate->getTimestamp() - $now->getTimestamp() -){ - //정지 상태 & 중간 넘음 - AppConf::getList()[DB::prefix()]->closeServer(); - $status = 'closed'; -} -else if( - $isUnited > 0 && - $now->getTimestamp() - $lastTurn->getTimestamp() > ($reservedDate->getTimestamp() - $now->getTimestamp()) * 2 -){ - //천통 & 비정지 상태 & 2/3 넘음 - AppConf::getList()[DB::prefix()]->closeServer(); - $status = 'closed'; -} -else if($reservedDate->getTimestamp() - $now->getTimestamp() <= 60*10){ - //어쨌든 간에 10분 남았다면. - AppConf::getList()[DB::prefix()]->closeServer(); - $status = 'closed'; -} - -if($now < $reservedDate){ - Json::die([ - 'result'=>true, - 'affected'=>0, - 'status'=>$status - ]); -} - -$options = Json::decode($reserved['options']); - -$result = ResetHelper::buildScenario( - $options['turnterm'], - $options['sync'], - $options['scenario'], - $options['fiction'], - $options['extend'], - $options['npcmode'], - $options['show_img_level'], - $options['tournament_trig'], - $options['join_mode'], - $options['starttime'], - $options['autorun_user']?:null -); - -$result['affected']=1; - -$prefix = DB::prefix(); -AppConf::getList()[$prefix]->openServer(); - +queryFirstField("SHOW TABLES LIKE 'reserved_open'")) { + Json::die([ + 'result'=>true, + 'affected'=>0, + 'status'=>'no_reserved_table' + ]); +} + +$reserved = $db->queryFirstRow('SELECT `date`, options FROM reserved_open ORDER BY `date` ASC LIMIT 1'); + +if(!$reserved){ + Json::die([ + 'result'=>true, + 'affected'=>0, + 'status'=>'no_reserved' + ]); +} + +$reservedDate = new \DateTime($reserved['date']); +$now = new \DateTime(); + + +$status = 'not_yet'; + +list($isUnited, $lastTurn) = $gameStor->getValuesAsArray(['isunited', 'turntime']); +if($isUnited === null || $lastTurn === null){ + $isUnited = 2; + $lastTurn = '2000-01-01'; +} + +if($lastTurn !== null){ + $lastTurn = new \DateTime($lastTurn); +} + +if($lastTurn === null){ + //이미 리셋된 상태임 +} +else if(file_exists(__DIR__.'/.htaccess')){ + //일단 서버는 닫혀 있음 +} +else if( + $isUnited == 2 && + $now->getTimestamp() - $lastTurn->getTimestamp() > $reservedDate->getTimestamp() - $now->getTimestamp() +){ + //정지 상태 & 중간 넘음 + ServConfig::getServerList()[DB::prefix()]->closeServer(); + $status = 'closed'; +} +else if( + $isUnited > 0 && + $now->getTimestamp() - $lastTurn->getTimestamp() > ($reservedDate->getTimestamp() - $now->getTimestamp()) * 2 +){ + //천통 & 비정지 상태 & 2/3 넘음 + ServConfig::getServerList()[DB::prefix()]->closeServer(); + $status = 'closed'; +} +else if($reservedDate->getTimestamp() - $now->getTimestamp() <= 60*10){ + //어쨌든 간에 10분 남았다면. + ServConfig::getServerList()[DB::prefix()]->closeServer(); + $status = 'closed'; +} + +if($now < $reservedDate){ + Json::die([ + 'result'=>true, + 'affected'=>0, + 'status'=>$status + ]); +} + +$options = Json::decode($reserved['options']); + +$result = ResetHelper::buildScenario( + $options['turnterm'], + $options['sync'], + $options['scenario'], + $options['fiction'], + $options['extend'], + $options['npcmode'], + $options['show_img_level'], + $options['tournament_trig'], + $options['join_mode'], + $options['starttime'], + $options['autorun_user']?:null +); + +$result['affected']=1; + +$prefix = DB::prefix(); +ServConfig::getServerList()[$prefix]->openServer(); + Json::die($result); \ No newline at end of file diff --git a/hwe/j_install.php b/hwe/j_install.php index 63dc1455..e69aaaaf 100644 --- a/hwe/j_install.php +++ b/hwe/j_install.php @@ -1,209 +1,209 @@ -setReadOnly(); - -if(!class_exists('\\sammo\\DB')){ - Json::die([ - 'result'=>false, - 'reason'=>'DB리셋 필요' - ]); -} - -$serverName = DB::prefix(); -$serverAcl = $session->acl[$serverName]??[]; -$allowReset = in_array('reset', $serverAcl); -$allowFullReset = in_array('fullReset',$serverAcl); -$allowReset |= $allowFullReset; - -$reserve_open = Util::getPost('reserve_open'); -if($reserve_open && $reserve_open < date('Y-m-d H:i')){ - Json::die([ - 'result'=>false, - 'reason'=>'현재 시간보다 이전 시간대를 예약 시간으로 지정했습니다.' - ]); -} - -$pre_reserve_open = Util::getPost('pre_reserve_open'); -if($pre_reserve_open && !$reserve_open){ - Json::die([ - 'result'=>false, - 'reason'=>'가오픈 예약을 위해선 오픈 예약을 지정해야합니다.' - ]); -} -if($pre_reserve_open && $pre_reserve_open >= $reserve_open){ - Json::die([ - 'result'=>false, - 'reason'=>'가오픈 시간이 오픈 예약 시점보다 이전이어야 합니다.' - ]); -} - -if($session->userGrade < 5 && !$allowReset){ - Json::die([ - 'result'=>false, - 'reason'=>'관리자 아님' - ]); -} - -$v = new Validator($_POST); -$v->rule('required', [ - 'turnterm', - 'sync', - 'scenario', - 'fiction', - 'extend', - 'join_mode', - 'npcmode', - 'show_img_level', - 'autorun_user_minutes' -])->rule('integer', [ - 'turnterm', - 'sync', - 'scenario', - 'fiction', - 'extend', - 'npcmode', - 'show_img_level', - 'tournament_trig', - 'autorun_user_minutes' -])->rule('in', 'join_mode', ['onlyRandom', 'full']); -if(!$v->validate()){ - Json::die([ - 'result'=>false, - 'reason'=>$v->errorStr() - ]); -} - -$allowReset = true; -if($session->userGrade < 5 && !$allowFullReset){ - //리셋 가능한 조건인지 테스트 - $allowReset = false; - - if(file_exists(__DIR__.'/.htaccess')){ - $allowReset = true; - } - else{ - $db = DB::db(); - $gameStor = KVStorage::getStorage($db, 'game_env'); - if($gameStor->isunited){ - $allowReset = true; - } - } -} - -if(!$allowReset){ - Json::die([ - 'result'=>false, - 'reason'=>'부족한 권한: 서버가 닫혀있거나, 천통되어 있을 경우에만 리셋 가능합니다.' - ]); -} - -$turnterm = (int)$_POST['turnterm']; -$sync = (int)$_POST['sync']; -$scenario = (int)$_POST['scenario']; -$fiction = (int)$_POST['fiction']; -$extend = (int)$_POST['extend']; -$npcmode = (int)$_POST['npcmode']; -$show_img_level = (int)$_POST['show_img_level']; -$tournament_trig = (int)$_POST['tournament_trig']; -$join_mode = $_POST['join_mode']; -$autorun_user_minutes = (int)$_POST['autorun_user_minutes']; -$autorun_user_options = []; -foreach(Util::getPost('autorun_user', 'array_string', []) as $autorun_option){ - $autorun_user_options[$autorun_option] = 1; -} - -if($autorun_user_minutes > 0 && !$autorun_user_options){ - Json::die([ - 'result'=>false, - 'reason'=>'적어도 자동 행동 중 하나는 선택을 해야합니다.' - ]); -} - -if($autorun_user_minutes < 0){ - Json::die([ - 'result'=>false, - 'reason'=>'자동 행동 기한이 0보다 작을 수 없습니다.' - ]); -} - -$autorun_user = $autorun_user_minutes?[ - 'limit_minutes'=>$autorun_user_minutes, - 'options'=>$autorun_user_options -]:null; - -if($reserve_open){ - $reserve_open = new \DateTime($reserve_open); - $db = DB::db(); - - if (!$db->queryFirstField("SHOW TABLES LIKE 'storage'")) { - $clearResult = ResetHelper::clearDB(); - if(!$clearResult['result']){ - Json::die($clearResult); - } - } - - if (!$db->queryFirstField("SHOW TABLES LIKE 'reserved_open'")) { - Json::die([ - 'result'=>false, - 'reason'=>'예약 테이블이 없음!' - ]); - } - - $scenarioObj = new Scenario($scenario, true); - $open_date = $reserve_open->format('Y-m-d H:i:s'); - - $reserveInfo = [ - 'turnterm'=>$turnterm, - 'sync'=>$sync, - 'scenario'=>$scenario, - 'scenarioName'=>$scenarioObj->getTitle(), - 'fiction'=>$fiction, - 'extend'=>$extend, - 'npcmode'=>$npcmode, - 'show_img_level'=>$show_img_level, - 'tournament_trig'=>$tournament_trig, - 'gameConf'=>$scenarioObj->getGameConf(), - 'join_mode'=>$join_mode, - 'starttime'=>$open_date, - 'autorun_user'=>$autorun_user - ]; - - - if($pre_reserve_open){ - $pre_reserve_open = new \DateTime($pre_reserve_open); - $open_date = $pre_reserve_open->format('Y-m-d H:i:s'); - } - - - - $db->delete('reserved_open', true); - $db->insert('reserved_open', [ - 'options'=>Json::encode($reserveInfo), - 'date'=>$open_date - ]); - AppConf::getList()[DB::prefix()]->closeServer(); - Json::die([ - 'result'=>true, - 'reason'=>'예약' - ]); -} - -Json::die(ResetHelper::buildScenario( - $turnterm, - $sync, - $scenario, - $fiction, - $extend, - $npcmode, - $show_img_level, - $tournament_trig, - $join_mode, - TimeUtil::now(), - $autorun_user +setReadOnly(); + +if(!class_exists('\\sammo\\DB')){ + Json::die([ + 'result'=>false, + 'reason'=>'DB리셋 필요' + ]); +} + +$serverName = DB::prefix(); +$serverAcl = $session->acl[$serverName]??[]; +$allowReset = in_array('reset', $serverAcl); +$allowFullReset = in_array('fullReset',$serverAcl); +$allowReset |= $allowFullReset; + +$reserve_open = Util::getPost('reserve_open'); +if($reserve_open && $reserve_open < date('Y-m-d H:i')){ + Json::die([ + 'result'=>false, + 'reason'=>'현재 시간보다 이전 시간대를 예약 시간으로 지정했습니다.' + ]); +} + +$pre_reserve_open = Util::getPost('pre_reserve_open'); +if($pre_reserve_open && !$reserve_open){ + Json::die([ + 'result'=>false, + 'reason'=>'가오픈 예약을 위해선 오픈 예약을 지정해야합니다.' + ]); +} +if($pre_reserve_open && $pre_reserve_open >= $reserve_open){ + Json::die([ + 'result'=>false, + 'reason'=>'가오픈 시간이 오픈 예약 시점보다 이전이어야 합니다.' + ]); +} + +if($session->userGrade < 5 && !$allowReset){ + Json::die([ + 'result'=>false, + 'reason'=>'관리자 아님' + ]); +} + +$v = new Validator($_POST); +$v->rule('required', [ + 'turnterm', + 'sync', + 'scenario', + 'fiction', + 'extend', + 'join_mode', + 'npcmode', + 'show_img_level', + 'autorun_user_minutes' +])->rule('integer', [ + 'turnterm', + 'sync', + 'scenario', + 'fiction', + 'extend', + 'npcmode', + 'show_img_level', + 'tournament_trig', + 'autorun_user_minutes' +])->rule('in', 'join_mode', ['onlyRandom', 'full']); +if(!$v->validate()){ + Json::die([ + 'result'=>false, + 'reason'=>$v->errorStr() + ]); +} + +$allowReset = true; +if($session->userGrade < 5 && !$allowFullReset){ + //리셋 가능한 조건인지 테스트 + $allowReset = false; + + if(file_exists(__DIR__.'/.htaccess')){ + $allowReset = true; + } + else{ + $db = DB::db(); + $gameStor = KVStorage::getStorage($db, 'game_env'); + if($gameStor->isunited){ + $allowReset = true; + } + } +} + +if(!$allowReset){ + Json::die([ + 'result'=>false, + 'reason'=>'부족한 권한: 서버가 닫혀있거나, 천통되어 있을 경우에만 리셋 가능합니다.' + ]); +} + +$turnterm = (int)$_POST['turnterm']; +$sync = (int)$_POST['sync']; +$scenario = (int)$_POST['scenario']; +$fiction = (int)$_POST['fiction']; +$extend = (int)$_POST['extend']; +$npcmode = (int)$_POST['npcmode']; +$show_img_level = (int)$_POST['show_img_level']; +$tournament_trig = (int)$_POST['tournament_trig']; +$join_mode = $_POST['join_mode']; +$autorun_user_minutes = (int)$_POST['autorun_user_minutes']; +$autorun_user_options = []; +foreach(Util::getPost('autorun_user', 'array_string', []) as $autorun_option){ + $autorun_user_options[$autorun_option] = 1; +} + +if($autorun_user_minutes > 0 && !$autorun_user_options){ + Json::die([ + 'result'=>false, + 'reason'=>'적어도 자동 행동 중 하나는 선택을 해야합니다.' + ]); +} + +if($autorun_user_minutes < 0){ + Json::die([ + 'result'=>false, + 'reason'=>'자동 행동 기한이 0보다 작을 수 없습니다.' + ]); +} + +$autorun_user = $autorun_user_minutes?[ + 'limit_minutes'=>$autorun_user_minutes, + 'options'=>$autorun_user_options +]:null; + +if($reserve_open){ + $reserve_open = new \DateTime($reserve_open); + $db = DB::db(); + + if (!$db->queryFirstField("SHOW TABLES LIKE 'storage'")) { + $clearResult = ResetHelper::clearDB(); + if(!$clearResult['result']){ + Json::die($clearResult); + } + } + + if (!$db->queryFirstField("SHOW TABLES LIKE 'reserved_open'")) { + Json::die([ + 'result'=>false, + 'reason'=>'예약 테이블이 없음!' + ]); + } + + $scenarioObj = new Scenario($scenario, true); + $open_date = $reserve_open->format('Y-m-d H:i:s'); + + $reserveInfo = [ + 'turnterm'=>$turnterm, + 'sync'=>$sync, + 'scenario'=>$scenario, + 'scenarioName'=>$scenarioObj->getTitle(), + 'fiction'=>$fiction, + 'extend'=>$extend, + 'npcmode'=>$npcmode, + 'show_img_level'=>$show_img_level, + 'tournament_trig'=>$tournament_trig, + 'gameConf'=>$scenarioObj->getGameConf(), + 'join_mode'=>$join_mode, + 'starttime'=>$open_date, + 'autorun_user'=>$autorun_user + ]; + + + if($pre_reserve_open){ + $pre_reserve_open = new \DateTime($pre_reserve_open); + $open_date = $pre_reserve_open->format('Y-m-d H:i:s'); + } + + + + $db->delete('reserved_open', true); + $db->insert('reserved_open', [ + 'options'=>Json::encode($reserveInfo), + 'date'=>$open_date + ]); + ServConfig::getServerList()[DB::prefix()]->closeServer(); + Json::die([ + 'result'=>true, + 'reason'=>'예약' + ]); +} + +Json::die(ResetHelper::buildScenario( + $turnterm, + $sync, + $scenario, + $fiction, + $extend, + $npcmode, + $show_img_level, + $tournament_trig, + $join_mode, + TimeUtil::now(), + $autorun_user )); \ No newline at end of file diff --git a/hwe/j_install_db.php b/hwe/j_install_db.php index 11908fea..64d23225 100644 --- a/hwe/j_install_db.php +++ b/hwe/j_install_db.php @@ -1,103 +1,103 @@ -setReadOnly(); -if($session->userGrade < 6){ - Json::die([ - 'result'=>false, - 'reason'=>'관리자 아님' - ]); -} - -$fullReset = Util::getPost('full_reset', 'bool', false); -$host = Util::getPost('db_host'); -$port = Util::getPost('db_port', 'int'); -$username = Util::getPost('db_id'); -$password = Util::getPost('db_pw'); -$dbName = Util::getPost('db_name'); - -if(!$host || !$port || !$username || !$password || !$dbName){ - Json::die([ - 'result'=>false, - 'reason'=>'입력 값이 올바르지 않습니다' - ]); -} - -if($fullReset && class_exists('\\sammo\\DB')){ - $mysqli_obj = DB::db()->get(); - - if($mysqli_obj->multi_query(file_get_contents(__DIR__.'/sql/reset.sql'))){ - while(true){ - if (!$mysqli_obj->more_results()) { - break; - } - if(!$mysqli_obj->next_result()){ - break; - } - } - } -} -if($fullReset){ - FileUtil::delInDir(__DIR__."/logs"); - FileUtil::delInDir(__DIR__."/data"); - if(file_exists(__DIR__.'/d_setting/DB.php')){ - @unlink(__DIR__.'/d_setting/DB.php'); - } - if(file_exists(__DIR__.'/d_setting/UniqueConst.php')){ - @unlink(__DIR__.'/d_setting/UniqueConst.php'); - } -} - -function dbConnFail($params){ - Json::die([ - 'result'=>false, - 'reason'=>'DB 접속에 실패했습니다.' - ]); -} - - -$db = new \MeekroDB($host,$username,$password,$dbName,$port,'utf8mb4'); -$db->connect_options[MYSQLI_OPT_INT_AND_FLOAT_NATIVE] = true; - -$db->throw_exception_on_nonsql_error = false; -$db->nonsql_error_handler = 'dbConnFail'; - - -$mysqli_obj = $db->get(); //로그인에 실패할 경우 자동으로 dbConnFail()이 실행됨. - -$prefix = basename(__DIR__); - -$result = Util::generateFileUsingSimpleTemplate( - __DIR__.'/d_setting/DB.orig.php', - __DIR__.'/d_setting/DB.php',[ - 'host'=>$host, - 'user'=>$username, - 'password'=>$password, - 'dbName'=>$dbName, - 'port'=>$port, - 'prefix'=>$prefix - ], true -); - - - -if($result !== true){ - Json::die([ - 'result'=>false, - 'reason'=>$result - ]); -} - -ResetHelper::clearDB(); - -AppConf::getList()[$prefix]->closeServer(); - -Json::die([ - 'result'=>true, - 'reason'=>'success' +setReadOnly(); +if($session->userGrade < 6){ + Json::die([ + 'result'=>false, + 'reason'=>'관리자 아님' + ]); +} + +$fullReset = Util::getPost('full_reset', 'bool', false); +$host = Util::getPost('db_host'); +$port = Util::getPost('db_port', 'int'); +$username = Util::getPost('db_id'); +$password = Util::getPost('db_pw'); +$dbName = Util::getPost('db_name'); + +if(!$host || !$port || !$username || !$password || !$dbName){ + Json::die([ + 'result'=>false, + 'reason'=>'입력 값이 올바르지 않습니다' + ]); +} + +if($fullReset && class_exists('\\sammo\\DB')){ + $mysqli_obj = DB::db()->get(); + + if($mysqli_obj->multi_query(file_get_contents(__DIR__.'/sql/reset.sql'))){ + while(true){ + if (!$mysqli_obj->more_results()) { + break; + } + if(!$mysqli_obj->next_result()){ + break; + } + } + } +} +if($fullReset){ + FileUtil::delInDir(__DIR__."/logs"); + FileUtil::delInDir(__DIR__."/data"); + if(file_exists(__DIR__.'/d_setting/DB.php')){ + @unlink(__DIR__.'/d_setting/DB.php'); + } + if(file_exists(__DIR__.'/d_setting/UniqueConst.php')){ + @unlink(__DIR__.'/d_setting/UniqueConst.php'); + } +} + +function dbConnFail($params){ + Json::die([ + 'result'=>false, + 'reason'=>'DB 접속에 실패했습니다.' + ]); +} + + +$db = new \MeekroDB($host,$username,$password,$dbName,$port,'utf8mb4'); +$db->connect_options[MYSQLI_OPT_INT_AND_FLOAT_NATIVE] = true; + +$db->throw_exception_on_nonsql_error = false; +$db->nonsql_error_handler = 'dbConnFail'; + + +$mysqli_obj = $db->get(); //로그인에 실패할 경우 자동으로 dbConnFail()이 실행됨. + +$prefix = basename(__DIR__); + +$result = Util::generateFileUsingSimpleTemplate( + __DIR__.'/d_setting/DB.orig.php', + __DIR__.'/d_setting/DB.php',[ + 'host'=>$host, + 'user'=>$username, + 'password'=>$password, + 'dbName'=>$dbName, + 'port'=>$port, + 'prefix'=>$prefix + ], true +); + + + +if($result !== true){ + Json::die([ + 'result'=>false, + 'reason'=>$result + ]); +} + +ResetHelper::clearDB(); + +ServConfig::getServerList()[$prefix]->closeServer(); + +Json::die([ + 'result'=>true, + 'reason'=>'success' ]); \ No newline at end of file diff --git a/hwe/sammo/ResetHelper.php b/hwe/sammo/ResetHelper.php index ddaecf07..94c0ecd7 100644 --- a/hwe/sammo/ResetHelper.php +++ b/hwe/sammo/ResetHelper.php @@ -1,358 +1,358 @@ -false, - 'reason'=>'logs, data 디렉토리를 생성할 권한이 없습니다.' - ]; - } - mkdir($servRoot.'/logs', 0755); - mkdir($servRoot.'/data', 0755); - } - - if(!is_writable($servRoot.'/logs')){ - return [ - 'result'=>false, - 'reason'=>'logs 디렉토리의 쓰기 권한이 없습니다' - ]; - } - - if(!is_writable($servRoot.'/data')){ - return [ - 'result'=>false, - 'reason'=>'data 디렉토리의 쓰기 권한이 없습니다' - ]; - } - - if(!is_writable($servRoot.'/d_setting')){ - return [ - 'result'=>false, - 'reason'=>'d_setting 디렉토리의 쓰기 권한이 없습니다' - ]; - } - - if(!file_exists($servRoot.'/logs/preserved')){ - mkdir($servRoot.'/logs/preserved', 0755); - } - - if(!file_exists($servRoot.'/logs/.htaccess')){ - @file_put_contents($servRoot.'/logs/.htaccess', 'Deny from all'); - } - - if(!file_exists($servRoot.'/data/.htaccess')){ - @file_put_contents($servRoot.'/data/.htaccess', 'Deny from all'); - } - - $dir = new \DirectoryIterator($servRoot.'/logs'); - foreach ($dir as $fileinfo) { - /** @var \DirectoryIterator $fileinfo */ - if (!$fileinfo->isDir() || $fileinfo->isDot()) { - continue; - } - $basename = $fileinfo->getFilename(); - if($basename == 'preserved'){ - continue; - } - if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { - exec("move ".escapeshellarg($servRoot.'/logs/'.$basename)." ".escapeshellarg($servRoot.'/logs/preserved/'.$basename)); - } else { - exec("mv ".escapeshellarg($servRoot.'/logs/'.$basename)." ".escapeshellarg($servRoot.'/logs/preserved/'.$basename)); - } - } - - $prefix = DB::prefix(); - AppConf::getList()[$prefix]->closeServer(); - - $db = DB::db(); - $mysqli_obj = $db->get(); - - $serverID = DB::prefix().'_'.date("ymd").'_'.Util::randomStr(4); - - mkdir($servRoot.'/logs/'.$serverID, 0755); - mkdir($servRoot.'/data/'.$serverID, 0755); - - $seasonIdx = 1; - if($db->queryFirstField('SHOW TABLES LIKE %s', 'storage') !== null){ - $gameStor = KVStorage::getStorage($db, 'game_env'); - $nextSeasonIdx = $gameStor->next_season_idx; - if($nextSeasonIdx !== null){ - $seasonIdx = $nextSeasonIdx; - } - $gameStor->resetCache(); - } - - $result = Util::generateFileUsingSimpleTemplate( - $servRoot.'/d_setting/UniqueConst.orig.php', - $servRoot.'/d_setting/UniqueConst.php',[ - 'serverID'=>$serverID, - 'serverName'=>AppConf::getList()[$prefix]->getKorName(), - 'seasonIdx'=>$seasonIdx, - ], true - ); - - copy( - $servRoot.'/d_setting/GameConst.orig.php', - $servRoot.'/d_setting/GameConst.php', - ); - - if($mysqli_obj->multi_query(file_get_contents($servRoot.'/sql/reset.sql'))){ - while(true){ - if (!$mysqli_obj->more_results()) { - break; - } - if(!$mysqli_obj->next_result()){ - break; - } - } - - } - - if($mysqli_obj->multi_query(file_get_contents($servRoot.'/sql/schema.sql'))){ - while(true){ - if (!$mysqli_obj->more_results()) { - break; - } - if(!$mysqli_obj->next_result()){ - break; - } - } - } - - $gameStor = KVStorage::getStorage($db, 'game_env'); - $gameStor->resetValues(); - $gameStor->next_season_idx = $seasonIdx; - - return [ - 'result'=>true, - 'serverID'=>$serverID, - 'seasonIdx'=>$seasonIdx - ]; - } - - static public function buildScenario( - int $turnterm, - int $sync, - int $scenario, - int $fiction, - int $extend, - int $npcmode, - int $show_img_level, - int $tournament_trig, - string $join_mode, - string $turntime, - ?array $autorun_user - ):array{ - //FIXME: 분리할 것 - if(120 % $turnterm != 0){ - return [ - 'result'=>false, - 'reason'=>'turnterm은 120의 약수여야 합니다.' - ]; - } - - if($tournament_trig < 0 || $tournament_trig > 7){ - return [ - 'result'=>false, - 'reason'=>'올바르지 않은 토너먼트 주기입니다.' - ]; - } - - $clearResult = self::clearDB(); - if(!$clearResult['result']){ - return $clearResult; - } - - $serverID = $clearResult['serverID']; - $seasonIdx = $clearResult['seasonIdx']; - - $scenarioObj = new Scenario($scenario, false); - $scenarioObj->buildConf(); - - $startyear = $scenarioObj->getYear()??GameConst::$defaultStartYear; - - $db = DB::db(); - $gameStor = KVStorage::getStorage($db, 'game_env'); - $db->insert('plock', [ - 'plock'=>1, - 'locktime'=>TimeUtil::now(true) - ]); - - $prevWinner = $db->queryFirstField('SELECT l12name FROM emperior ORDER BY `no` DESC LIMIT 1'); - $serverCnt = $db->queryFirstField('SELECT count(*) FROM ng_games') + 1; - - CityConst::build(); - $cityPositions = []; - foreach(CityConst::all() as $city){ - $cityPositions[$city->id] = [ - $city->name, - $city->posX, - $city->posY - ]; - } - Util::generateFileUsingSimpleTemplate( - __DIR__.'/../templates/base_map.orig.js', - __DIR__.'/../d_shared/base_map.js', - [ - 'cityPosition'=>Json::encode($cityPositions), - 'regionMap'=>Json::encode(CityConst::$regionMap), - 'levelMap'=>Json::encode(CityConst::$levelMap), - ], - true - ); - - if($sync == 0) { - // 현재 시간을 1월로 맞춤 - $starttime = cutTurn($turntime, $turnterm); - $month = 1; - $year = $startyear; - } else { - // 현재 시간과 동기화 - [$starttime, $yearPulled, $month] = cutDay($turntime, $turnterm); - if($yearPulled){ - $year = $startyear-1; - } - else{ - $year = $startyear; - } - } - - $killturn = 4800 / $turnterm; - if($npcmode == 1) { $killturn = intdiv($killturn, 3); } - - $develcost = ($year - $startyear + 10) * 2; - - $env = [ - 'scenario'=>$scenario, - 'scenario_text'=>$scenarioObj->getTitle(), - 'icon_path'=>$scenarioObj->getIconPath(), - 'startyear'=>$startyear, - 'year'=> $year, - 'month'=> $month, - 'init_year'=> $year, - 'init_month'=>$month, - 'map_theme' => $scenarioObj->getMapTheme(), - 'season'=>$seasonIdx, - 'msg'=>'공지사항',//TODO:공지사항 - 'maxgeneral'=>GameConst::$defaultMaxGeneral, - 'maxnation'=>GameConst::$defaultMaxNation, - 'conlimit'=>30000, - 'develcost'=>$develcost, - 'turntime'=>$turntime, - 'starttime'=>$starttime, - 'opentime'=>$turntime, - 'turnterm'=>$turnterm, - 'killturn'=>$killturn, - 'genius'=>GameConst::$defaultMaxGenius, - 'show_img_level'=>$show_img_level, - 'join_mode'=>$join_mode, - 'npcmode'=>$npcmode, - 'extended_general'=>$extend, - 'fiction'=>$fiction, - 'tnmt_trig'=>$tournament_trig, - 'prev_winner'=>$prevWinner, - 'autorun_user'=>$autorun_user, - 'tournament'=>0, - 'server_cnt'=>$serverCnt, - ]; - - $db->insert('betting', [ - 'general_id'=>0, - 'bet0'=>0, - 'bet1'=>0, - 'bet2'=>0, - 'bet3'=>0, - 'bet4'=>0, - 'bet5'=>0, - 'bet6'=>0, - 'bet7'=>0, - 'bet8'=>0, - 'bet9'=>0, - 'bet10'=>0, - 'bet11'=>0, - 'bet12'=>0, - 'bet13'=>0, - 'bet14'=>0, - 'bet15'=>0, - ]); - - foreach(RootDB::db()->query('SELECT `no`, `name`, `picture`, `imgsvr` FROM member WHERE grade >= 5') as $admin){ - $db->insert('general', [ - 'owner'=>$admin['no'], - 'name'=>$admin['name'], - 'picture'=>$admin['picture'], - 'imgsvr'=>$admin['imgsvr'], - 'turntime'=>$turntime, - 'killturn'=>9999, - 'crewtype'=>GameUnitConst::DEFAULT_CREWTYPE - ]); - $generalID = $db->insertId(); - $turnRows = []; - foreach(Util::range(GameConst::$maxTurn) as $turnIdx){ - $turnRows[] = [ - 'general_id'=>$generalID, - 'turn_idx'=>$turnIdx, - 'action'=>'휴식', - 'arg'=>null, - 'brief'=>'휴식' - ]; - } - $db->insert('general_turn', $turnRows); - - - $rank_data = []; - foreach(array_keys(General::RANK_COLUMN) as $rankColumn){ - $rank_data[] = [ - 'general_id'=>$generalID, - 'nation_id'=>0, - 'type'=>$rankColumn, - 'value'=>0 - ]; - } - $db->insert('rank_data', $rank_data); - $db->insert('betting', [ - 'general_id'=>$generalID, - ]); - } - - foreach($env as $key=>$value){ - $gameStor->$key = $value; - } - - $db->insert('ng_games', [ - 'server_id'=>$serverID, - 'date'=>$turntime, - 'winner_nation'=>null, - 'map'=>$scenarioObj->getMapTheme(), - 'season'=>$seasonIdx, - 'scenario'=>$scenario, - 'scenario_name'=>$scenarioObj->getTitle(), - 'env'=>Json::encode($env) - ]); - - $scenarioObj->build($env); - - $db->update('plock', [ - 'plock'=>0 - ], true); - - LogHistory(1); - - $prefix = DB::prefix(); - AppConf::getList()[$prefix]->closeServer(); - opcache_reset(); - - return [ - 'result'=>true - ]; - } +false, + 'reason'=>'logs, data 디렉토리를 생성할 권한이 없습니다.' + ]; + } + mkdir($servRoot.'/logs', 0755); + mkdir($servRoot.'/data', 0755); + } + + if(!is_writable($servRoot.'/logs')){ + return [ + 'result'=>false, + 'reason'=>'logs 디렉토리의 쓰기 권한이 없습니다' + ]; + } + + if(!is_writable($servRoot.'/data')){ + return [ + 'result'=>false, + 'reason'=>'data 디렉토리의 쓰기 권한이 없습니다' + ]; + } + + if(!is_writable($servRoot.'/d_setting')){ + return [ + 'result'=>false, + 'reason'=>'d_setting 디렉토리의 쓰기 권한이 없습니다' + ]; + } + + if(!file_exists($servRoot.'/logs/preserved')){ + mkdir($servRoot.'/logs/preserved', 0755); + } + + if(!file_exists($servRoot.'/logs/.htaccess')){ + @file_put_contents($servRoot.'/logs/.htaccess', 'Deny from all'); + } + + if(!file_exists($servRoot.'/data/.htaccess')){ + @file_put_contents($servRoot.'/data/.htaccess', 'Deny from all'); + } + + $dir = new \DirectoryIterator($servRoot.'/logs'); + foreach ($dir as $fileinfo) { + /** @var \DirectoryIterator $fileinfo */ + if (!$fileinfo->isDir() || $fileinfo->isDot()) { + continue; + } + $basename = $fileinfo->getFilename(); + if($basename == 'preserved'){ + continue; + } + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + exec("move ".escapeshellarg($servRoot.'/logs/'.$basename)." ".escapeshellarg($servRoot.'/logs/preserved/'.$basename)); + } else { + exec("mv ".escapeshellarg($servRoot.'/logs/'.$basename)." ".escapeshellarg($servRoot.'/logs/preserved/'.$basename)); + } + } + + $prefix = DB::prefix(); + ServConfig::getServerList()[$prefix]->closeServer(); + + $db = DB::db(); + $mysqli_obj = $db->get(); + + $serverID = DB::prefix().'_'.date("ymd").'_'.Util::randomStr(4); + + mkdir($servRoot.'/logs/'.$serverID, 0755); + mkdir($servRoot.'/data/'.$serverID, 0755); + + $seasonIdx = 1; + if($db->queryFirstField('SHOW TABLES LIKE %s', 'storage') !== null){ + $gameStor = KVStorage::getStorage($db, 'game_env'); + $nextSeasonIdx = $gameStor->next_season_idx; + if($nextSeasonIdx !== null){ + $seasonIdx = $nextSeasonIdx; + } + $gameStor->resetCache(); + } + + $result = Util::generateFileUsingSimpleTemplate( + $servRoot.'/d_setting/UniqueConst.orig.php', + $servRoot.'/d_setting/UniqueConst.php',[ + 'serverID'=>$serverID, + 'serverName'=>ServConfig::getServerList()[$prefix]->getKorName(), + 'seasonIdx'=>$seasonIdx, + ], true + ); + + copy( + $servRoot.'/d_setting/GameConst.orig.php', + $servRoot.'/d_setting/GameConst.php', + ); + + if($mysqli_obj->multi_query(file_get_contents($servRoot.'/sql/reset.sql'))){ + while(true){ + if (!$mysqli_obj->more_results()) { + break; + } + if(!$mysqli_obj->next_result()){ + break; + } + } + + } + + if($mysqli_obj->multi_query(file_get_contents($servRoot.'/sql/schema.sql'))){ + while(true){ + if (!$mysqli_obj->more_results()) { + break; + } + if(!$mysqli_obj->next_result()){ + break; + } + } + } + + $gameStor = KVStorage::getStorage($db, 'game_env'); + $gameStor->resetValues(); + $gameStor->next_season_idx = $seasonIdx; + + return [ + 'result'=>true, + 'serverID'=>$serverID, + 'seasonIdx'=>$seasonIdx + ]; + } + + static public function buildScenario( + int $turnterm, + int $sync, + int $scenario, + int $fiction, + int $extend, + int $npcmode, + int $show_img_level, + int $tournament_trig, + string $join_mode, + string $turntime, + ?array $autorun_user + ):array{ + //FIXME: 분리할 것 + if(120 % $turnterm != 0){ + return [ + 'result'=>false, + 'reason'=>'turnterm은 120의 약수여야 합니다.' + ]; + } + + if($tournament_trig < 0 || $tournament_trig > 7){ + return [ + 'result'=>false, + 'reason'=>'올바르지 않은 토너먼트 주기입니다.' + ]; + } + + $clearResult = self::clearDB(); + if(!$clearResult['result']){ + return $clearResult; + } + + $serverID = $clearResult['serverID']; + $seasonIdx = $clearResult['seasonIdx']; + + $scenarioObj = new Scenario($scenario, false); + $scenarioObj->buildConf(); + + $startyear = $scenarioObj->getYear()??GameConst::$defaultStartYear; + + $db = DB::db(); + $gameStor = KVStorage::getStorage($db, 'game_env'); + $db->insert('plock', [ + 'plock'=>1, + 'locktime'=>TimeUtil::now(true) + ]); + + $prevWinner = $db->queryFirstField('SELECT l12name FROM emperior ORDER BY `no` DESC LIMIT 1'); + $serverCnt = $db->queryFirstField('SELECT count(*) FROM ng_games') + 1; + + CityConst::build(); + $cityPositions = []; + foreach(CityConst::all() as $city){ + $cityPositions[$city->id] = [ + $city->name, + $city->posX, + $city->posY + ]; + } + Util::generateFileUsingSimpleTemplate( + __DIR__.'/../templates/base_map.orig.js', + __DIR__.'/../d_shared/base_map.js', + [ + 'cityPosition'=>Json::encode($cityPositions), + 'regionMap'=>Json::encode(CityConst::$regionMap), + 'levelMap'=>Json::encode(CityConst::$levelMap), + ], + true + ); + + if($sync == 0) { + // 현재 시간을 1월로 맞춤 + $starttime = cutTurn($turntime, $turnterm); + $month = 1; + $year = $startyear; + } else { + // 현재 시간과 동기화 + [$starttime, $yearPulled, $month] = cutDay($turntime, $turnterm); + if($yearPulled){ + $year = $startyear-1; + } + else{ + $year = $startyear; + } + } + + $killturn = 4800 / $turnterm; + if($npcmode == 1) { $killturn = intdiv($killturn, 3); } + + $develcost = ($year - $startyear + 10) * 2; + + $env = [ + 'scenario'=>$scenario, + 'scenario_text'=>$scenarioObj->getTitle(), + 'icon_path'=>$scenarioObj->getIconPath(), + 'startyear'=>$startyear, + 'year'=> $year, + 'month'=> $month, + 'init_year'=> $year, + 'init_month'=>$month, + 'map_theme' => $scenarioObj->getMapTheme(), + 'season'=>$seasonIdx, + 'msg'=>'공지사항',//TODO:공지사항 + 'maxgeneral'=>GameConst::$defaultMaxGeneral, + 'maxnation'=>GameConst::$defaultMaxNation, + 'conlimit'=>30000, + 'develcost'=>$develcost, + 'turntime'=>$turntime, + 'starttime'=>$starttime, + 'opentime'=>$turntime, + 'turnterm'=>$turnterm, + 'killturn'=>$killturn, + 'genius'=>GameConst::$defaultMaxGenius, + 'show_img_level'=>$show_img_level, + 'join_mode'=>$join_mode, + 'npcmode'=>$npcmode, + 'extended_general'=>$extend, + 'fiction'=>$fiction, + 'tnmt_trig'=>$tournament_trig, + 'prev_winner'=>$prevWinner, + 'autorun_user'=>$autorun_user, + 'tournament'=>0, + 'server_cnt'=>$serverCnt, + ]; + + $db->insert('betting', [ + 'general_id'=>0, + 'bet0'=>0, + 'bet1'=>0, + 'bet2'=>0, + 'bet3'=>0, + 'bet4'=>0, + 'bet5'=>0, + 'bet6'=>0, + 'bet7'=>0, + 'bet8'=>0, + 'bet9'=>0, + 'bet10'=>0, + 'bet11'=>0, + 'bet12'=>0, + 'bet13'=>0, + 'bet14'=>0, + 'bet15'=>0, + ]); + + foreach(RootDB::db()->query('SELECT `no`, `name`, `picture`, `imgsvr` FROM member WHERE grade >= 5') as $admin){ + $db->insert('general', [ + 'owner'=>$admin['no'], + 'name'=>$admin['name'], + 'picture'=>$admin['picture'], + 'imgsvr'=>$admin['imgsvr'], + 'turntime'=>$turntime, + 'killturn'=>9999, + 'crewtype'=>GameUnitConst::DEFAULT_CREWTYPE + ]); + $generalID = $db->insertId(); + $turnRows = []; + foreach(Util::range(GameConst::$maxTurn) as $turnIdx){ + $turnRows[] = [ + 'general_id'=>$generalID, + 'turn_idx'=>$turnIdx, + 'action'=>'휴식', + 'arg'=>null, + 'brief'=>'휴식' + ]; + } + $db->insert('general_turn', $turnRows); + + + $rank_data = []; + foreach(array_keys(General::RANK_COLUMN) as $rankColumn){ + $rank_data[] = [ + 'general_id'=>$generalID, + 'nation_id'=>0, + 'type'=>$rankColumn, + 'value'=>0 + ]; + } + $db->insert('rank_data', $rank_data); + $db->insert('betting', [ + 'general_id'=>$generalID, + ]); + } + + foreach($env as $key=>$value){ + $gameStor->$key = $value; + } + + $db->insert('ng_games', [ + 'server_id'=>$serverID, + 'date'=>$turntime, + 'winner_nation'=>null, + 'map'=>$scenarioObj->getMapTheme(), + 'season'=>$seasonIdx, + 'scenario'=>$scenario, + 'scenario_name'=>$scenarioObj->getTitle(), + 'env'=>Json::encode($env) + ]); + + $scenarioObj->build($env); + + $db->update('plock', [ + 'plock'=>0 + ], true); + + LogHistory(1); + + $prefix = DB::prefix(); + ServConfig::getServerList()[$prefix]->closeServer(); + opcache_reset(); + + return [ + 'result'=>true + ]; + } } \ No newline at end of file diff --git a/i_entrance/j_get_user_info.php b/i_entrance/j_get_user_info.php index 45ff4a48..5a48dc77 100644 --- a/i_entrance/j_get_user_info.php +++ b/i_entrance/j_get_user_info.php @@ -1,90 +1,90 @@ -setReadOnly(); -$userID = Session::getUserID(); - -// 외부 파라미터 - -$db = RootDB::db(); -$member = $db->queryFirstRow('SELECT `id`, `name`, `grade`, `picture`, reg_date, third_use, acl, oauth_type, token_valid_until FROM `member` WHERE `NO` = %i', $userID); - -if(!$member['picture']){ - $picture = ServConfig::getSharedIconPath().'/default.jpg'; -} -else{ - $picture = $member['picture']; - if(strlen($picture) > 11){ - $picture = substr($picture, 0, -10); - } - - $pictureFSPath = AppConf::getUserIconPathFS().'/'.$picture; - - if(file_exists($pictureFSPath)){ - $picture = AppConf::getUserIconPathWeb().'/'.$picture; - } - else{ - $picture = ServConfig::getSharedIconPath().'/'.$picture; - } -} - -$tokenValidUntil = $member['token_valid_until']; - -if($member['grade'] == 6) { - $grade = '운영자'; -} elseif($member['grade'] == 5) { - $grade = '부운영자'; -} elseif($member['grade'] > 1) { - $grade = '특별회원'; -} elseif($member['grade'] == 1) { - $grade = '일반회원'; -} elseif($member['grade'] == 0) { - $grade = '블럭회원'; -} - -$acl = []; -//TODO: Acl을 관리하기 위한 별도의 객체 필요 -foreach(Json::decode($member['acl']??'{}') as $serverName=>$aclList){ - $serverKorName = AppConf::getList()[$serverName]->getKorName(); - $aclTextList = array_map(function($aclName){ - $aclText = "알수없음[{$aclName}]"; - switch($aclName){ - case 'openClose':$aclText='서버여닫기';break; - case 'reset':$aclText='서버리셋';break; - case 'update':$aclText='업데이트';break; - case 'fullUpdate':$aclText='임의업데이트';break; - case 'vote':$aclText='설문조사';break; - case 'globalNotice':$aclText='전역공지';break; - case 'notice':$aclText='공지';break; - case 'blockGeneral':$aclText='장수징계';break; - } - return $aclText; - }, $aclList); - - $acl[] = sprintf("%s(%s)", $serverKorName, join(",", $aclTextList)); -} - -if($acl){ - $acl = join(",
", $acl); -} -else{ - $acl = "-"; -} - -Json::die([ - 'result'=>true, - 'reason'=>'success', - 'id'=>$member['id'], - 'name'=>$member['name'], - 'grade'=>$grade, - 'picture'=>$picture, - 'global_salt'=>RootDB::getGlobalSalt(), - 'join_date'=>$member['reg_date'], - 'third_use'=>($member['third_use']!=0), - 'acl'=>$acl, - 'oauth_type'=>$member['oauth_type'], - 'token_valid_until'=>$tokenValidUntil - -]); +setReadOnly(); +$userID = Session::getUserID(); + +// 외부 파라미터 + +$db = RootDB::db(); +$member = $db->queryFirstRow('SELECT `id`, `name`, `grade`, `picture`, reg_date, third_use, acl, oauth_type, token_valid_until FROM `member` WHERE `NO` = %i', $userID); + +if(!$member['picture']){ + $picture = ServConfig::getSharedIconPath().'/default.jpg'; +} +else{ + $picture = $member['picture']; + if(strlen($picture) > 11){ + $picture = substr($picture, 0, -10); + } + + $pictureFSPath = AppConf::getUserIconPathFS().'/'.$picture; + + if(file_exists($pictureFSPath)){ + $picture = AppConf::getUserIconPathWeb().'/'.$picture; + } + else{ + $picture = ServConfig::getSharedIconPath().'/'.$picture; + } +} + +$tokenValidUntil = $member['token_valid_until']; + +if($member['grade'] == 6) { + $grade = '운영자'; +} elseif($member['grade'] == 5) { + $grade = '부운영자'; +} elseif($member['grade'] > 1) { + $grade = '특별회원'; +} elseif($member['grade'] == 1) { + $grade = '일반회원'; +} elseif($member['grade'] == 0) { + $grade = '블럭회원'; +} + +$acl = []; +//TODO: Acl을 관리하기 위한 별도의 객체 필요 +foreach(Json::decode($member['acl']??'{}') as $serverName=>$aclList){ + $serverKorName = ServConfig::getServerList()[$serverName]->getKorName(); + $aclTextList = array_map(function($aclName){ + $aclText = "알수없음[{$aclName}]"; + switch($aclName){ + case 'openClose':$aclText='서버여닫기';break; + case 'reset':$aclText='서버리셋';break; + case 'update':$aclText='업데이트';break; + case 'fullUpdate':$aclText='임의업데이트';break; + case 'vote':$aclText='설문조사';break; + case 'globalNotice':$aclText='전역공지';break; + case 'notice':$aclText='공지';break; + case 'blockGeneral':$aclText='장수징계';break; + } + return $aclText; + }, $aclList); + + $acl[] = sprintf("%s(%s)", $serverKorName, join(",", $aclTextList)); +} + +if($acl){ + $acl = join(",
", $acl); +} +else{ + $acl = "-"; +} + +Json::die([ + 'result'=>true, + 'reason'=>'success', + 'id'=>$member['id'], + 'name'=>$member['name'], + 'grade'=>$grade, + 'picture'=>$picture, + 'global_salt'=>RootDB::getGlobalSalt(), + 'join_date'=>$member['reg_date'], + 'third_use'=>($member['third_use']!=0), + 'acl'=>$acl, + 'oauth_type'=>$member['oauth_type'], + 'token_valid_until'=>$tokenValidUntil + +]); diff --git a/i_entrance/j_get_userlist.php b/i_entrance/j_get_userlist.php index 505112f8..03c39bcb 100644 --- a/i_entrance/j_get_userlist.php +++ b/i_entrance/j_get_userlist.php @@ -1,58 +1,58 @@ -setReadOnly(); -$db = RootDB::db(); - -if($session->userGrade < 5){ - Json::die([ - 'result'=>false, - 'reason'=>'권한이 부족합니다' - ]); -} - -$userList = []; -foreach($db->query('SELECT member.*, max(member_log.date) as loginDate from member - left join member_log on member.`NO` = member_log.member_no and member_log.action_type="login" - group by member.no order by member.no asc') as $member){ - - if($member['IMGSVR']){ - $icon = AppConf::getUserIconPathWeb().'/'.$member['PICTURE']; - } - else{ - $icon = ServConfig::getSharedIconPath().'/'.$member['PICTURE']; - } - - $userList[] = [ - 'userID'=>$member['NO'], - 'userName'=>$member['ID'], - 'email'=>$member['EMAIL'], - 'authType'=>$member['oauth_type'], - 'userGrade'=>$member['GRADE'], - 'blockUntil'=>$member['BLOCK_DATE'], - 'nickname'=>$member['NAME'], - 'icon'=>$icon, - 'joinDate'=>$member['REG_DATE'], - 'loginDate'=>$member['loginDate'], - 'deleteAfter'=>$member['delete_after'] - ]; -} - -$serverList = []; -foreach(AppConf::getList() as $serverName => $setting){ - if($setting->isRunning()){ - $serverList[] = $serverName; - } -} - -$system = $db->queryFirstRow('SELECT `REG`, `LOGIN` FROM `system` limit 1'); - -Json::die([ - 'result'=>true, - 'users'=>$userList, - 'servers'=>$serverList, - 'allowJoin'=>$system['REG']=='Y', - 'allowLogin'=>$system['LOGIN']=='Y' +setReadOnly(); +$db = RootDB::db(); + +if($session->userGrade < 5){ + Json::die([ + 'result'=>false, + 'reason'=>'권한이 부족합니다' + ]); +} + +$userList = []; +foreach($db->query('SELECT member.*, max(member_log.date) as loginDate from member + left join member_log on member.`NO` = member_log.member_no and member_log.action_type="login" + group by member.no order by member.no asc') as $member){ + + if($member['IMGSVR']){ + $icon = AppConf::getUserIconPathWeb().'/'.$member['PICTURE']; + } + else{ + $icon = ServConfig::getSharedIconPath().'/'.$member['PICTURE']; + } + + $userList[] = [ + 'userID'=>$member['NO'], + 'userName'=>$member['ID'], + 'email'=>$member['EMAIL'], + 'authType'=>$member['oauth_type'], + 'userGrade'=>$member['GRADE'], + 'blockUntil'=>$member['BLOCK_DATE'], + 'nickname'=>$member['NAME'], + 'icon'=>$icon, + 'joinDate'=>$member['REG_DATE'], + 'loginDate'=>$member['loginDate'], + 'deleteAfter'=>$member['delete_after'] + ]; +} + +$serverList = []; +foreach(ServConfig::getServerList() as $serverName => $setting){ + if($setting->isRunning()){ + $serverList[] = $serverName; + } +} + +$system = $db->queryFirstRow('SELECT `REG`, `LOGIN` FROM `system` limit 1'); + +Json::die([ + 'result'=>true, + 'users'=>$userList, + 'servers'=>$serverList, + 'allowJoin'=>$system['REG']=='Y', + 'allowLogin'=>$system['LOGIN']=='Y' ]); \ No newline at end of file diff --git a/i_entrance/j_icon_change.php b/i_entrance/j_icon_change.php index 487c36a4..eed93411 100644 --- a/i_entrance/j_icon_change.php +++ b/i_entrance/j_icon_change.php @@ -1,107 +1,107 @@ -setReadOnly(); -$userID = Session::getUserID(); - -// 외부 파라미터 -// $_FILES['image_upload'] : 사진파일 - -$defaultImg = 'default.jpg'; - -$image = $_FILES['image_upload']; -$ext = strrchr($image['name'], "."); -$size = getImageSize($image['tmp_name']); - - - -$imageType = $size[2]; - -$availableImageType = array('.jpg'=>IMAGETYPE_JPEG, '.png'=>IMAGETYPE_PNG, '.gif'=>IMAGETYPE_GIF); - -$db = RootDB::db(); -$member = $db->queryFirstRow('SELECT `ID`, `PICTURE` FROM `member` WHERE `NO` = %i', $userID); - - -$picName = $member['PICTURE']; -$newExt = array_search($imageType, $availableImageType, true); - -if($picName && strlen($picName) > 11){ - $dt = substr($picName, -8); - $picName = substr($picName, 0, -10); -} -else{ - $dt = '00000000'; -} - -$rf = date('Ymd'); - -$response['result'] = false; -$response['reason'] = '요청이 올바르지 않습니다!'; -if(!is_uploaded_file($image['tmp_name'])) { - //진짜 전송된 파일인지 검증 - $response['reason'] = '업로드가 되지 않았습니다!'; - $response['result'] = false; -} elseif(!$newExt) { - //확장자 검사 - $response['reason'] = 'jpg, gif, png 파일이 아닙니다!'; - $response['result'] = false; -} elseif($image['size'] > 30720) { - //파일크기 검사 - $response['reason'] = '30kb 이하로 올려주세요!'; - $response['result'] = false; -} elseif($size[0] < 64 || 128 < $size[0]) { - //이미지크기 검사 - $response['reason'] = '64x64 ~ 128x128 사이여야 합니다.'; - $response['result'] = false; -} elseif($size[0] != $size[1]) { - //이미지크기 검사 - $response['reason'] = '1:1 비율 이미지여야 합니다.'; - $response['result'] = false; -}elseif($dt == $rf) { - //갱신날짜 검사 - $response['reason'] = '1일 1회 변경 가능합니다!'; - $response['result'] = false; -} else { - //이미지 저장 - - while(true){ - $newPicName = dechex(rand(0x000000f,0xfffffff)).$newExt; - $dest = AppConf::getUserIconPathFS().'/'.$newPicName; - if(file_exists($dest)){ - continue; - } - break; - } - - if(!move_uploaded_file($image['tmp_name'], $dest)) { - $response['reason'] = '업로드에 실패했습니다!'; - $response['result'] = false; - } else { - $pic = "{$newPicName}?={$rf}"; - RootDB::db()->update('member',[ - 'PICTURE' => $pic, - 'IMGSVR' => 1 - ], 'NO=%i', $userID); - - $servers = []; - - foreach(AppConf::getList() as $key=>$setting){ - if($setting->isRunning()){ - $servers[] = [$key, $setting->getKorName()]; - } - } - - $response['servers'] = $servers; - - $response['reason'] = '업로드에 성공했습니다!'; - $response['result'] = true; - } -} - - +setReadOnly(); +$userID = Session::getUserID(); + +// 외부 파라미터 +// $_FILES['image_upload'] : 사진파일 + +$defaultImg = 'default.jpg'; + +$image = $_FILES['image_upload']; +$ext = strrchr($image['name'], "."); +$size = getImageSize($image['tmp_name']); + + + +$imageType = $size[2]; + +$availableImageType = array('.jpg'=>IMAGETYPE_JPEG, '.png'=>IMAGETYPE_PNG, '.gif'=>IMAGETYPE_GIF); + +$db = RootDB::db(); +$member = $db->queryFirstRow('SELECT `ID`, `PICTURE` FROM `member` WHERE `NO` = %i', $userID); + + +$picName = $member['PICTURE']; +$newExt = array_search($imageType, $availableImageType, true); + +if($picName && strlen($picName) > 11){ + $dt = substr($picName, -8); + $picName = substr($picName, 0, -10); +} +else{ + $dt = '00000000'; +} + +$rf = date('Ymd'); + +$response['result'] = false; +$response['reason'] = '요청이 올바르지 않습니다!'; +if(!is_uploaded_file($image['tmp_name'])) { + //진짜 전송된 파일인지 검증 + $response['reason'] = '업로드가 되지 않았습니다!'; + $response['result'] = false; +} elseif(!$newExt) { + //확장자 검사 + $response['reason'] = 'jpg, gif, png 파일이 아닙니다!'; + $response['result'] = false; +} elseif($image['size'] > 30720) { + //파일크기 검사 + $response['reason'] = '30kb 이하로 올려주세요!'; + $response['result'] = false; +} elseif($size[0] < 64 || 128 < $size[0]) { + //이미지크기 검사 + $response['reason'] = '64x64 ~ 128x128 사이여야 합니다.'; + $response['result'] = false; +} elseif($size[0] != $size[1]) { + //이미지크기 검사 + $response['reason'] = '1:1 비율 이미지여야 합니다.'; + $response['result'] = false; +}elseif($dt == $rf) { + //갱신날짜 검사 + $response['reason'] = '1일 1회 변경 가능합니다!'; + $response['result'] = false; +} else { + //이미지 저장 + + while(true){ + $newPicName = dechex(rand(0x000000f,0xfffffff)).$newExt; + $dest = AppConf::getUserIconPathFS().'/'.$newPicName; + if(file_exists($dest)){ + continue; + } + break; + } + + if(!move_uploaded_file($image['tmp_name'], $dest)) { + $response['reason'] = '업로드에 실패했습니다!'; + $response['result'] = false; + } else { + $pic = "{$newPicName}?={$rf}"; + RootDB::db()->update('member',[ + 'PICTURE' => $pic, + 'IMGSVR' => 1 + ], 'NO=%i', $userID); + + $servers = []; + + foreach(ServConfig::getServerList() as $key=>$setting){ + if($setting->isRunning()){ + $servers[] = [$key, $setting->getKorName()]; + } + } + + $response['servers'] = $servers; + + $response['reason'] = '업로드에 성공했습니다!'; + $response['result'] = true; + } +} + + Json::die($response); \ No newline at end of file diff --git a/i_entrance/j_icon_delete.php b/i_entrance/j_icon_delete.php index 4b1ae842..bc4914ce 100644 --- a/i_entrance/j_icon_delete.php +++ b/i_entrance/j_icon_delete.php @@ -1,56 +1,56 @@ -setReadOnly(); -$userID = Session::getUserID(); - -// 외부 파라미터 - -$respone = []; -$db = RootDB::db(); -$picName = $db->queryFirstField('SELECT picture FROM `member` WHERE `NO` = %i', $userID); - -if($picName && strlen($picName) > 11){ - $dt = substr($picName, -8); - $picName = substr($picName, 0, -10); -} -else{ - $dt = '00000000'; -} - -$dest = AppConf::getUserIconPathFS().'/'.$picName; - -$rf = date('Ymd'); - -$response['result'] = false; -$response['reason'] = '요청이 올바르지 않습니다!'; - -if($dt == $rf) { - //갱신날짜 검사 - $response['reason'] = '1일 1회 변경 가능합니다!'; - $response['result'] = false; -} else { - $db->update('member', [ - 'PICTURE'=>'default.jpg', - 'IMGSVR'=>0, - ], 'NO=%i', $userID); - - $servers = []; - - foreach(AppConf::getList() as $key=>$setting){ - - if($setting->isRunning()){ - $servers[] = [$key, $setting->getKorName()]; - } - } - - $response['servers'] = $servers; - $response['reason'] = '제거에 성공했습니다!'; - $response['result'] = true; -} - +setReadOnly(); +$userID = Session::getUserID(); + +// 외부 파라미터 + +$respone = []; +$db = RootDB::db(); +$picName = $db->queryFirstField('SELECT picture FROM `member` WHERE `NO` = %i', $userID); + +if($picName && strlen($picName) > 11){ + $dt = substr($picName, -8); + $picName = substr($picName, 0, -10); +} +else{ + $dt = '00000000'; +} + +$dest = AppConf::getUserIconPathFS().'/'.$picName; + +$rf = date('Ymd'); + +$response['result'] = false; +$response['reason'] = '요청이 올바르지 않습니다!'; + +if($dt == $rf) { + //갱신날짜 검사 + $response['reason'] = '1일 1회 변경 가능합니다!'; + $response['result'] = false; +} else { + $db->update('member', [ + 'PICTURE'=>'default.jpg', + 'IMGSVR'=>0, + ], 'NO=%i', $userID); + + $servers = []; + + foreach(ServConfig::getServerList() as $key=>$setting){ + + if($setting->isRunning()){ + $servers[] = [$key, $setting->getKorName()]; + } + } + + $response['servers'] = $servers; + $response['reason'] = '제거에 성공했습니다!'; + $response['result'] = true; +} + Json::die($response); \ No newline at end of file diff --git a/i_entrance/j_server_change_status.php b/i_entrance/j_server_change_status.php index 22b2bc14..982bd581 100644 --- a/i_entrance/j_server_change_status.php +++ b/i_entrance/j_server_change_status.php @@ -1,117 +1,117 @@ -userGrade; -$acl = $session->acl; -$session->setReadOnly(); - -if($userGrade < 5 && !$acl) { - Json::die([ - 'result'=>'FAIL', - 'msg'=>'운영자 권한이 없습니다.' - ]); -} - -function doServerModeSet($server, $action, &$response, $session){ - - $serverList = AppConf::getList(); - $settingObj = $serverList[$server]; - $serverAcl = $session->acl[$server]??[]; - $userGrade = $session->userGrade; - - $serverDir = $settingObj->getShortName(); - $serverPath = $settingObj->getBasePath(); - $realServerPath = realpath(dirname(__FILE__)).'/'.$serverPath; - - if($action == 'close') { //폐쇄 - $doClose = false; - if($userGrade >= 5){ - $doClose = true; - } - else if(in_array('openClose', $serverAcl)){ - $doClose = true; - } - - if(!$doClose && in_array('reset', $serverAcl) && file_exists($serverPath.'/d_setting/DB.php')){ - require($serverPath.'/lib.php'); - $localGameStorage = KVStorage::getStorage(DB::db(), 'game_env'); - //천통 이후, 오픈 직후는 닫을 수 있음 - $localGameStorage->cacheValues(['isunited', 'startyear', 'year']); - - if($localGameStorage->isunited){ - $doClose = true; - } - else if($localGameStorage->year < $localGameStorage->startyear + 2){ - $doClose = true; - } - - } - - if(!$doClose){ - if(in_array('reset', $serverAcl)){ - $response['msg'] = '서버 시작 직후, 또는 천하통일 이후에만 닫을 수 있습니다.'; - } - else{ - $response['msg'] = '서버 닫기 권한이 부족합니다.'; - } - return false; - } - return $settingObj->closeServer(); - } elseif($action == 'reset' && $userGrade >= 6) {//리셋 - //FIXME: reset, reset_full 구현 - if(file_exists($serverPath.'/d_setting/DB.php')){ - @unlink($serverPath.'/d_setting/DB.php'); - } - - $response['installURL'] = $serverDir."/install.php"; - } elseif($action == 'open' && ($userGrade >= 5 || in_array('openClose', $serverAcl))) {//오픈 - return $settingObj->openServer(); - } else{ - $response['msg'] = '올바르지 않은 요청입니다'; - return false; - } - return true; -} - -function doAdminPost($action, $notice, $server, $session){ - $response = ['result' => 'FAIL']; - - $globalAcl = $session->acl['global']??[]; - $userGrade = $session->userGrade; - - if($action == 'notice' && ($userGrade >= 5 || in_array('notice', $globalAcl))) { - RootDB::db()->update('system', ['NOTICE'=>$notice], true); - $response['result'] = 'SUCCESS'; - return $response; - } - - if(doServerModeSet($server, $action, $response, $session)){ - $response['result'] = 'SUCCESS'; - return $response; - } - - return $response; - -} - -$response = doAdminPost($action, $notice, $server, $session); - -Json::die($response); +userGrade; +$acl = $session->acl; +$session->setReadOnly(); + +if($userGrade < 5 && !$acl) { + Json::die([ + 'result'=>'FAIL', + 'msg'=>'운영자 권한이 없습니다.' + ]); +} + +function doServerModeSet($server, $action, &$response, $session){ + + $serverList = ServConfig::getServerList(); + $settingObj = $serverList[$server]; + $serverAcl = $session->acl[$server]??[]; + $userGrade = $session->userGrade; + + $serverDir = $settingObj->getShortName(); + $serverPath = $settingObj->getBasePath(); + $realServerPath = realpath(dirname(__FILE__)).'/'.$serverPath; + + if($action == 'close') { //폐쇄 + $doClose = false; + if($userGrade >= 5){ + $doClose = true; + } + else if(in_array('openClose', $serverAcl)){ + $doClose = true; + } + + if(!$doClose && in_array('reset', $serverAcl) && file_exists($serverPath.'/d_setting/DB.php')){ + require($serverPath.'/lib.php'); + $localGameStorage = KVStorage::getStorage(DB::db(), 'game_env'); + //천통 이후, 오픈 직후는 닫을 수 있음 + $localGameStorage->cacheValues(['isunited', 'startyear', 'year']); + + if($localGameStorage->isunited){ + $doClose = true; + } + else if($localGameStorage->year < $localGameStorage->startyear + 2){ + $doClose = true; + } + + } + + if(!$doClose){ + if(in_array('reset', $serverAcl)){ + $response['msg'] = '서버 시작 직후, 또는 천하통일 이후에만 닫을 수 있습니다.'; + } + else{ + $response['msg'] = '서버 닫기 권한이 부족합니다.'; + } + return false; + } + return $settingObj->closeServer(); + } elseif($action == 'reset' && $userGrade >= 6) {//리셋 + //FIXME: reset, reset_full 구현 + if(file_exists($serverPath.'/d_setting/DB.php')){ + @unlink($serverPath.'/d_setting/DB.php'); + } + + $response['installURL'] = $serverDir."/install.php"; + } elseif($action == 'open' && ($userGrade >= 5 || in_array('openClose', $serverAcl))) {//오픈 + return $settingObj->openServer(); + } else{ + $response['msg'] = '올바르지 않은 요청입니다'; + return false; + } + return true; +} + +function doAdminPost($action, $notice, $server, $session){ + $response = ['result' => 'FAIL']; + + $globalAcl = $session->acl['global']??[]; + $userGrade = $session->userGrade; + + if($action == 'notice' && ($userGrade >= 5 || in_array('notice', $globalAcl))) { + RootDB::db()->update('system', ['NOTICE'=>$notice], true); + $response['result'] = 'SUCCESS'; + return $response; + } + + if(doServerModeSet($server, $action, $response, $session)){ + $response['result'] = 'SUCCESS'; + return $response; + } + + return $response; + +} + +$response = doAdminPost($action, $notice, $server, $session); + +Json::die($response); diff --git a/i_entrance/j_server_get_admin_status.php b/i_entrance/j_server_get_admin_status.php index 575430d7..ca8b13e3 100644 --- a/i_entrance/j_server_get_admin_status.php +++ b/i_entrance/j_server_get_admin_status.php @@ -1,90 +1,90 @@ -setReadOnly(); -if($session->userGrade < 5 && !$session->acl){ - Json::die([ - 'result'=>false, - 'reason'=>'권한이 부족합니다.' - ]); -} - -$result = []; - -$server = []; - -$storage = new \sammo\KVStorage(RootDB::db(), 'git_path'); -$serverGitPath = $storage->getAll(); - -$rootServer = Util::array_last(AppConf::getList())->getShortName(); - -foreach (AppConf::getList() as $setting) { - $serverColor = $setting->getColor(); - $serverKorName = $setting->getKorName(); - - $serverPath = $setting->getBasePath(); - $serverDir = $setting->getShortName(); - //TODO: .htaccess가 서버 오픈에도 사용될 수 있으니 별도의 방법이 필요함 - if (!is_dir($serverPath)) { - $state = [ - 'valid'=>false, - 'run'=>false, - 'installed'=>false, - 'version'=>$setting->getVersion(), - 'reason'=>'디렉토리 없음' - ]; - } elseif (!file_exists($serverPath.'/index.php')) { - $state = [ - 'valid'=>false, - 'run'=>false, - 'installed'=>false, - 'version'=>$setting->getVersion(), - 'reason'=>'index.php 없음' - ]; - } elseif (!$setting->isExists()) { - $state = [ - 'valid'=>false, - 'run'=>false, - 'installed'=>true, - 'version'=>$setting->getVersion(), - 'reason'=>'설정 파일 없음' - ]; - } elseif (!$setting->isRunning()) { - // 폐쇄중 - $state = [ - 'valid'=>true, - 'run'=>false, - 'installed'=>true, - 'version'=>$setting->getVersion(), - 'reason'=>'폐쇄됨' - ]; - } else { - // 오픈중 - $state = [ - 'valid'=>true, - 'run'=>true, - 'installed'=>true, - 'version'=>$setting->getVersion(), - 'reason'=>'운영중' - ]; - } - - $state += [ - 'name' => $serverDir, - 'korName' => $serverKorName, - 'color' => $serverColor, - 'isRoot' => $serverDir == $rootServer, - 'lastGitPath' => ($serverGitPath[$serverDir][0])??($serverDir == $rootServer?'devel':'origin/devel') - ]; - $server[] = $state; -} - -Json::die([ - 'acl' => $session->acl, - 'server' => $server, - 'grade' => $session->userGrade +setReadOnly(); +if($session->userGrade < 5 && !$session->acl){ + Json::die([ + 'result'=>false, + 'reason'=>'권한이 부족합니다.' + ]); +} + +$result = []; + +$server = []; + +$storage = new \sammo\KVStorage(RootDB::db(), 'git_path'); +$serverGitPath = $storage->getAll(); + +$rootServer = Util::array_last(ServConfig::getServerList())->getShortName(); + +foreach (ServConfig::getServerList() as $setting) { + $serverColor = $setting->getColor(); + $serverKorName = $setting->getKorName(); + + $serverPath = $setting->getBasePath(); + $serverDir = $setting->getShortName(); + //TODO: .htaccess가 서버 오픈에도 사용될 수 있으니 별도의 방법이 필요함 + if (!is_dir($serverPath)) { + $state = [ + 'valid'=>false, + 'run'=>false, + 'installed'=>false, + 'version'=>$setting->getVersion(), + 'reason'=>'디렉토리 없음' + ]; + } elseif (!file_exists($serverPath.'/index.php')) { + $state = [ + 'valid'=>false, + 'run'=>false, + 'installed'=>false, + 'version'=>$setting->getVersion(), + 'reason'=>'index.php 없음' + ]; + } elseif (!$setting->isExists()) { + $state = [ + 'valid'=>false, + 'run'=>false, + 'installed'=>true, + 'version'=>$setting->getVersion(), + 'reason'=>'설정 파일 없음' + ]; + } elseif (!$setting->isRunning()) { + // 폐쇄중 + $state = [ + 'valid'=>true, + 'run'=>false, + 'installed'=>true, + 'version'=>$setting->getVersion(), + 'reason'=>'폐쇄됨' + ]; + } else { + // 오픈중 + $state = [ + 'valid'=>true, + 'run'=>true, + 'installed'=>true, + 'version'=>$setting->getVersion(), + 'reason'=>'운영중' + ]; + } + + $state += [ + 'name' => $serverDir, + 'korName' => $serverKorName, + 'color' => $serverColor, + 'isRoot' => $serverDir == $rootServer, + 'lastGitPath' => ($serverGitPath[$serverDir][0])??($serverDir == $rootServer?'devel':'origin/devel') + ]; + $server[] = $state; +} + +Json::die([ + 'acl' => $session->acl, + 'server' => $server, + 'grade' => $session->userGrade ]); \ No newline at end of file diff --git a/i_entrance/j_server_get_status.php b/i_entrance/j_server_get_status.php index c5b05bf2..4a765843 100644 --- a/i_entrance/j_server_get_status.php +++ b/i_entrance/j_server_get_status.php @@ -1,25 +1,25 @@ -$setting->getColor(), - 'korName'=>$setting->getKorName(), - 'name'=>$setting->getShortName(), - 'exists'=>$setting->isExists(), - 'enable'=>$setting->isRunning() - ]; - - $response['server'][] = $serverObj; -} - -$response['result'] = 'SUCCESS'; -Json::die($response); - - +$setting->getColor(), + 'korName'=>$setting->getKorName(), + 'name'=>$setting->getShortName(), + 'exists'=>$setting->isExists(), + 'enable'=>$setting->isRunning() + ]; + + $response['server'][] = $serverObj; +} + +$response['result'] = 'SUCCESS'; +Json::die($response); + + diff --git a/j_updateServer.php b/j_updateServer.php index 7cad2607..ed426d18 100644 --- a/j_updateServer.php +++ b/j_updateServer.php @@ -1,240 +1,240 @@ -setReadOnly(); - -$request = $_POST + $_GET; - -$rootDB = RootDB::db(); -$storage = new \sammo\KVStorage($rootDB, 'git_path'); -$tmpFile = 'd_log/arc.zip'; - -$v = new Validator($request); - -$v->rule('required', [ - 'server' -])->rule('regex', 'target', '/^[0-9a-zA-Z^{}\\/\-_,.@]+$/'); - -if (!$v->validate()) { - Json::die([ - 'result' => false, - 'reason' => $v->errorStr() - ]); -} - -$target = Util::getPost('target'); - -$server = basename($request['server']); - -$allowFullUpdate = in_array('fullUpdate', $session->acl[$server] ?? []); -$allowFullUpdate |= $session->userGrade >= 6; - -$allowUpdate = in_array('update', $session->acl[$server] ?? []); -$allowUpdate |= $session->userGrade >= 5; -$allowUpdate |= $allowFullUpdate; - - -if (!$allowUpdate) { - Json::die([ - 'result' => false, - 'reason' => '권한이 충분하지 않습니다' - ]); -} - -$src_target = $storage->$server; -if ($src_target) { - $src_target = $src_target[0]; -} - -if (!$allowFullUpdate || !$target) { - $target = $src_target; -} else { - $target = $request['target']; -} - -$baseServerName = Util::array_last_key(AppConf::getList()); - -if (!$target && $server != $baseServerName) { - Json::die([ - 'result' => false, - 'reason' => 'git -ish target이 제대로 지정되지 않았습니다.' - ]); -} - - -$targetDir = $target . ':' . $baseServerName; - -if (!key_exists($server, AppConf::getList())) { - Json::die([ - 'result' => false, - 'reason' => '불가능한 서버 이름입니다.' - ]); -} - -if (\file_exists($server) && !is_dir($server)) { - Json::die([ - 'result' => false, - 'reason' => '같은 이름을 가진 파일이 있습니다.' - ]); -} - -if (file_exists($server) && !is_writable($server)) { - Json::die([ - 'result' => false, - 'reason' => $server . ' 디렉토리 쓰기 권한이 없습니다.' - ]); -} - -if (!file_exists($server)) { - if (!is_writable('.')) { - Json::die([ - 'result' => false, - 'reason' => $server . ' 디렉토리가 없지만 생성할 권한이 없습니다.' - ]); - } - mkdir($server, 0755); -} - - -if ($server == $baseServerName) { - - exec("git fetch -q 2>&1", $output); - if ($output) { - Json::die([ - 'result' => false, - 'reason' => 'git pull 작업 : ' . join(', ', $output) - ]); - } - - if ($target != $src_target) { - $command = sprintf('git checkout %s -q 2>&1', $target); - exec($command, $output); - if ($output) { - Json::die([ - 'result' => false, - 'reason' => join(', ', $output) - ]); - } - } - - exec("git pull -q 2>&1", $output); - if ($output && $output[0] != 'Already up-to-date.') { - Json::die([ - 'result' => false, - 'reason' => 'git pull 작업 : ' . join(', ', $output) - ]); - } - - $version = getVersion(); - $result = Util::generateFileUsingSimpleTemplate( - __DIR__ . '/' . $server . '/d_setting/VersionGit.orig.php', - __DIR__ . '/' . $server . '/d_setting/VersionGit.php', - [ - 'verionGit' => $version - ], - true - ); - - if (ServConfig::$imageRequestKey) { - try { - $imagePullPath = ServConfig::getImagePullURI(); - $pullResult = @file_get_contents($imagePullPath); - if ($pullResult === false) { - throw new \ErrorException('Invalid URI'); - } - $pullResult = Json::decode($pullResult); - if ($pullResult['result']) { - $imgResult = true; - $imgDetail = $pullResult['version']; - } else { - $imgResult = false; - $imgDetail = $pullResult['reason']; - } - } catch (\Exception $e) { - $imgResult = false; - $imgDetail = $e->getMessage(); - } - } else { - $imgResult = true; - $imgDetail = 'No key'; - } - - - - $storage->$server = [$target, $version]; - opcache_reset(); - - Json::die([ - 'server' => $server, - 'result' => true, - 'version' => $version, - 'imgResult' => $imgResult, - 'imgDetail' => $imgDetail, - ]); -} - -$command = sprintf('git archive --format=zip -o %s %s', escapeshellarg($tmpFile), escapeshellarg($targetDir)); -exec("$command 2>&1", $output); -if ($output) { - Json::die([ - 'result' => false, - 'reason' => join(', ', $output) - ]); -} - -$zip = new \ZipArchive; -if ($zip->open($tmpFile) !== true) { - Json::die([ - 'result' => false, - 'reason' => 'archive가 생성되지 않았습니다.' - ]); -} - -if (!$zip->extractTo($server)) { - Json::die([ - 'result' => false, - 'reason' => '생성한 archive를 제대로 옮기지 못했습니다.' - ]); -} - -$zip->close(); -@unlink($tmpFile); - -$version = getVersion($target); -$result = Util::generateFileUsingSimpleTemplate( - __DIR__ . '/' . $server . '/d_setting/VersionGit.orig.php', - __DIR__ . '/' . $server . '/d_setting/VersionGit.php', - [ - 'verionGit' => $version - ], - true -); - -$storage->$server = [$target, $version]; -//AppConf::getList()[$server]->closeServer(); -opcache_reset(); - -Json::die([ - 'server' => $server, - 'result' => true, - 'version' => $version -]); +setReadOnly(); + +$request = $_POST + $_GET; + +$rootDB = RootDB::db(); +$storage = new \sammo\KVStorage($rootDB, 'git_path'); +$tmpFile = 'd_log/arc.zip'; + +$v = new Validator($request); + +$v->rule('required', [ + 'server' +])->rule('regex', 'target', '/^[0-9a-zA-Z^{}\\/\-_,.@]+$/'); + +if (!$v->validate()) { + Json::die([ + 'result' => false, + 'reason' => $v->errorStr() + ]); +} + +$target = Util::getPost('target'); + +$server = basename($request['server']); + +$allowFullUpdate = in_array('fullUpdate', $session->acl[$server] ?? []); +$allowFullUpdate |= $session->userGrade >= 6; + +$allowUpdate = in_array('update', $session->acl[$server] ?? []); +$allowUpdate |= $session->userGrade >= 5; +$allowUpdate |= $allowFullUpdate; + + +if (!$allowUpdate) { + Json::die([ + 'result' => false, + 'reason' => '권한이 충분하지 않습니다' + ]); +} + +$src_target = $storage->$server; +if ($src_target) { + $src_target = $src_target[0]; +} + +if (!$allowFullUpdate || !$target) { + $target = $src_target; +} else { + $target = $request['target']; +} + +$baseServerName = Util::array_last_key(ServConfig::getServerList()); + +if (!$target && $server != $baseServerName) { + Json::die([ + 'result' => false, + 'reason' => 'git -ish target이 제대로 지정되지 않았습니다.' + ]); +} + + +$targetDir = $target . ':' . $baseServerName; + +if (!key_exists($server, ServConfig::getServerList())) { + Json::die([ + 'result' => false, + 'reason' => '불가능한 서버 이름입니다.' + ]); +} + +if (\file_exists($server) && !is_dir($server)) { + Json::die([ + 'result' => false, + 'reason' => '같은 이름을 가진 파일이 있습니다.' + ]); +} + +if (file_exists($server) && !is_writable($server)) { + Json::die([ + 'result' => false, + 'reason' => $server . ' 디렉토리 쓰기 권한이 없습니다.' + ]); +} + +if (!file_exists($server)) { + if (!is_writable('.')) { + Json::die([ + 'result' => false, + 'reason' => $server . ' 디렉토리가 없지만 생성할 권한이 없습니다.' + ]); + } + mkdir($server, 0755); +} + + +if ($server == $baseServerName) { + + exec("git fetch -q 2>&1", $output); + if ($output) { + Json::die([ + 'result' => false, + 'reason' => 'git pull 작업 : ' . join(', ', $output) + ]); + } + + if ($target != $src_target) { + $command = sprintf('git checkout %s -q 2>&1', $target); + exec($command, $output); + if ($output) { + Json::die([ + 'result' => false, + 'reason' => join(', ', $output) + ]); + } + } + + exec("git pull -q 2>&1", $output); + if ($output && $output[0] != 'Already up-to-date.') { + Json::die([ + 'result' => false, + 'reason' => 'git pull 작업 : ' . join(', ', $output) + ]); + } + + $version = getVersion(); + $result = Util::generateFileUsingSimpleTemplate( + __DIR__ . '/' . $server . '/d_setting/VersionGit.orig.php', + __DIR__ . '/' . $server . '/d_setting/VersionGit.php', + [ + 'verionGit' => $version + ], + true + ); + + if (ServConfig::$imageRequestKey) { + try { + $imagePullPath = ServConfig::getImagePullURI(); + $pullResult = @file_get_contents($imagePullPath); + if ($pullResult === false) { + throw new \ErrorException('Invalid URI'); + } + $pullResult = Json::decode($pullResult); + if ($pullResult['result']) { + $imgResult = true; + $imgDetail = $pullResult['version']; + } else { + $imgResult = false; + $imgDetail = $pullResult['reason']; + } + } catch (\Exception $e) { + $imgResult = false; + $imgDetail = $e->getMessage(); + } + } else { + $imgResult = true; + $imgDetail = 'No key'; + } + + + + $storage->$server = [$target, $version]; + opcache_reset(); + + Json::die([ + 'server' => $server, + 'result' => true, + 'version' => $version, + 'imgResult' => $imgResult, + 'imgDetail' => $imgDetail, + ]); +} + +$command = sprintf('git archive --format=zip -o %s %s', escapeshellarg($tmpFile), escapeshellarg($targetDir)); +exec("$command 2>&1", $output); +if ($output) { + Json::die([ + 'result' => false, + 'reason' => join(', ', $output) + ]); +} + +$zip = new \ZipArchive; +if ($zip->open($tmpFile) !== true) { + Json::die([ + 'result' => false, + 'reason' => 'archive가 생성되지 않았습니다.' + ]); +} + +if (!$zip->extractTo($server)) { + Json::die([ + 'result' => false, + 'reason' => '생성한 archive를 제대로 옮기지 못했습니다.' + ]); +} + +$zip->close(); +@unlink($tmpFile); + +$version = getVersion($target); +$result = Util::generateFileUsingSimpleTemplate( + __DIR__ . '/' . $server . '/d_setting/VersionGit.orig.php', + __DIR__ . '/' . $server . '/d_setting/VersionGit.php', + [ + 'verionGit' => $version + ], + true +); + +$storage->$server = [$target, $version]; +//ServConfig::getServerList()[$server]->closeServer(); +opcache_reset(); + +Json::die([ + 'server' => $server, + 'result' => true, + 'version' => $version +]); diff --git a/src/sammo/AppConf.php b/src/sammo/AppConf.php index 1cc9a1b8..ab3162ff 100644 --- a/src/sammo/AppConf.php +++ b/src/sammo/AppConf.php @@ -1,79 +1,69 @@ -new Setting(ROOT.'/che', '체', 'white'), - 'kwe'=>new Setting(ROOT.'/kwe', '퀘', 'yellow'), - 'pwe'=>new Setting(ROOT.'/pwe', '풰', 'orange'), - 'twe'=>new Setting(ROOT.'/twe', '퉤', 'magenta'), - 'nya'=>new Setting(ROOT.'/nya', '냐', '#e67e22'), - 'pya'=>new Setting(ROOT.'/pya', '퍄', '#9b59b6'), - 'hwe'=>new Setting(ROOT.'/hwe', '훼', 'red') - ]; - } - return self::$serverList; - } - - /** - * DB 객체 생성 - * - * @return \MeekroDB - */ - public static function requireRootDB() - { - if (!class_exists('\\sammo\\RootDB')) { - if(!trigger_error('RootDB.php가 설정되지 않았습니다.', E_USER_ERROR)){ - die(); - } - - } - return RootDB::db(); - } - - /** - * DB 객체 생성 - * - * @return \MeekroDB - */ - public static function requireDB() - { - if (!class_exists('\\sammo\\DB')) { - if(!trigger_error('DB.php가 설정되지 않았습니다.', E_USER_ERROR)){ - die(); - } - - } - return DB::db(); - } - - public static function getUserIconPathFS(string $filepath='') : string{ - $path = ROOT.'/'.static::$userIconPath; - if($filepath){ - $path .= '/'.$filepath; - } - return $path; - } - - public static function getUserIconPathWeb(string $filepath='') : string{ - $path = ServConfig::$serverWebPath.'/'.static::$userIconPath; - if($filepath){ - $path .= '/'.$filepath; - } - return $path; - } -} +valid()){ - $values[] = $iter->send(NULL); - $hasValue = true; - } - } - yield $values; - - if(!$hasValue){ - break; - } - } - } - - /** - * $_POST, $_GET에서 값을 가져오는 함수. Util::array_get($_POST[$name])을 축약 가능. - * 타입이 복잡해질 경우 이 함수를 통하지 않고 json으로 요청할 것을 권장. - * - * @param string $name 가져오고자 하는 key 이름. - * @param string $type 가져오고자 하는 type. [string, int, float, bool, array, array_string, array_int] - * @param mixed $ifNotExists 만약 $_POST와 $_GET에 값이 없을 경우 반환하는 변수. 이 값은 $type을 검사하지 않음. - * @return int|float|string|array|null - * @throws \InvalidArgumentException - */ - public static function getReq(string $name, string $type = 'string', $ifNotExists = null) - { - if (isset($_POST[$name])) { - $value = $_POST[$name]; - } elseif (isset($_GET[$name])) { - $value = $_GET[$name]; - } else { - return $ifNotExists; - } - - return static::_parseReq($value, $type); - } - - /** - * $_POST에서 값을 가져오는 함수. Util::array_get($_POST[$name])을 축약 가능. $_GET에서도 가져올 수 있다면 getReq 사용. - * 타입이 복잡해질 경우 이 함수를 통하지 않고 json으로 요청할 것을 권장. - * - * @param string $name 가져오고자 하는 key 이름. - * @param string $type 가져오고자 하는 type. [string, int, float, bool, array, array_string, array_int] - * @param mixed $ifNotExists 만약 $_GET과 $_POST에 값이 없을 경우 반환하는 변수. 이 값은 $type을 검사하지 않음. - * @return int|float|string|array|null - * @throws \InvalidArgumentException - */ - public static function getPost(string $name, string $type = 'string', $ifNotExists = null) - { - if (isset($_POST[$name])) { - $value = $_POST[$name]; - } else { - return $ifNotExists; - } - - return static::_parseReq($value, $type); - } - - public static function hashPassword($salt, $password) - { - return hash('sha512', $salt.$password.$salt); - } - - /** - * 변환할 내용이 _tK_$key_ 형태로 작성된 단순한 템플릿 파일을 이용하여 결과물을 생성해주는 함수. - */ - public static function generateFileUsingSimpleTemplate(string $srcFilePath, string $destFilePath, array $params, bool $canOverwrite=false) - { - if ($destFilePath === $srcFilePath) { - return 'invalid destFilePath'; - } - if (!file_exists($srcFilePath)) { - return 'srcFilePath is not exists'; - } - if (file_exists($destFilePath) && !$canOverwrite) { - return 'destFilePath is already exists'; - } - if (!is_writable(dirname($destFilePath))) { - return 'destFilePath is not writable'; - } - - $text = file_get_contents($srcFilePath); - foreach ($params as $key => $value) { - $text = str_replace("_tK_{$key}_", $value, $text); - } - file_put_contents($destFilePath, $text); - - return true; - } - - /** - * params에 맞도록 class를 생성해주는 함수 - */ - public static function generatePHPClassFile(string $destFilePath, array $params, ?string $srcClassName=null, string $namespace='sammo'){ - if (!is_writable(dirname($destFilePath))) { - return 'destFilePath is not writable'; - } - - $newClassName = basename($destFilePath, '.php'); - $newClassName = basename($newClassName, '.orig'); - $head = []; - $head[] = '$value){ - - if(!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/',$key)){ - return "$key is not valid variable name"; - } - - $body[] = ' public static $'.$key.' = '.var_export($value, true).';'; - } - $tail = "\n}"; - - if(file_exists($destFilePath)){ - unlink($destFilePath); - } - $result = file_put_contents($destFilePath, $head.join("\n", $body).$tail, LOCK_EX); - assert($result); - - return true; - } - - /** - * '비교적' 안전한 int 변환 - * null -> null - * int -> int - * float -> int - * numeric(int, float) 포함 -> int - * 기타 -> 예외처리 - * - * @return int|null - */ - public static function toInt($val, $silent=false) - { - if (!isset($val)) { - return null; - } - if ($val === null) { - return null; - } - if (is_int($val)) { - return $val; - } - if (is_numeric($val)) { - return intval($val);// - } - if (strtolower($val) === 'null') { - return null; - } - - if ($silent) { - if ($val == null) { - return null; - } - if ($val == ''){ - return null; - } - return intval($val); - } - throw new \InvalidArgumentException('올바르지 않은 타입형 :'.$val); - } - - /** - * Generate a random string, using a cryptographically secure - * pseudorandom number generator (random_int) - * - * For PHP 7, random_int is a PHP core function - * For PHP 5.x, depends on https://github.com/paragonie/random_compat - * - * @param int $length How many characters do we want? - * @param string $keyspace A string of all possible characters - * to select from - * @return string - */ - public static function randomStr($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') - { - $str = ''; - $max = mb_strlen($keyspace, '8bit') - 1; - for ($i = 0; $i < $length; ++$i) { - $str .= $keyspace[random_int(0, $max)]; - } - return $str; - } - - public static function mapWithKey($callback, $dict) - { - $result = []; - foreach ($dict as $key=>$value) { - $result[$key] = ($callback)($key, $value); - } - return $result; - } - - public static function convertArrayToDict($arr, $keyName) - { - $result = []; - - foreach ($arr as $obj) { - $key = $obj[$keyName]; - $result[$key] = $obj; - } - - return $result; - } - - public static function convertArrayToSetLike($arr, $valueIsKey=true){ - $result = []; - foreach($arr as $datum){ - $result[$datum] = $valueIsKey?$datum:1; - } - return $result; - } - - public static function convertPairArrayToDict($arr){ - $result = []; - foreach($arr as [$key, $val]){ - $result[$key] = $val; - } - return $result; - } - - public static function convertDictToArray($dict, bool $withKey=true) - { - $result = []; - - foreach($dict as $key=>$value){ - if($withKey){ - $result[] = [$key, $value]; - } - else{ - $result[] = $value; - } - } - - return $result; - } - - public static function squeezeFromArray(array $dict, $key){ - $result = []; - foreach($dict as $dictKey=>$value){ - $result[$dictKey] = $value[$key]; - } - - return $result; - } - - public static function isDict($array) - { - if($array === null){ - return false; - } - if (!is_array($array)) { - //배열이 아니면 dictionary 조차 아님. - return false; - } - if(count($array) === 0){ - return true; - } - - $idx = 0; - foreach (array_keys($array) as $key) { - if (is_string($key)) { - return true; - } - if($idx !== $key){ - return true; - } - $idx = $key + 1; - } - - return false; - } - - /** - * @param null|mixed|mixed[] $dict - * @return null|mixed|mixed[] - */ - public static function eraseNullValue($dict, int $depth=512) - { - //TODO:Test 추가 - if ($dict === null) { - return null; - } - - if (is_array($dict) && empty($dict)) { - return null; - } - - if ($depth <= 0) { - return $dict; - } - - foreach ($dict as $key=>$value) { - if ($value === null) { - unset($dict[$key]); - continue; - } - if (!Util::isDict($value)) { - continue; - } - - $newValue = Util::eraseNullValue($value, $depth - 1); - if ($newValue === null) { - unset($dict[$key]); - } else { - $dict[$key] = $newValue; - } - } - - return $dict; - } - - /** - * key=>value pair를 보존한 섞기 - */ - public static function shuffle_assoc(&$array) { - $keys = array_keys($array); - - shuffle($keys); - - $new = []; - - foreach($keys as $key) { - $new[$key] = $array[$key]; - } - - $array = $new; - - return true; - } - - - /** - * [0.0, 1.0] 사이의 선형 랜덤 float - * @return float - */ - public static function randF() - { - return mt_rand() / mt_getrandmax(); - } - - /** - * [min, max] 사이의 선형 랜덤 float - * @return float - */ - public static function randRange(float $min, float $max) - { - return static::randF()*($max - $min) + $min; - } - - /** - * [min, max] 사이의 선형 랜덤 int - * 현재는 rand(min, max)와 동일 - * @return int - */ - public static function randRangeInt(int $min, int $max){ - return mt_rand($min, $max); - } - - /** - * $prob의 확률로 true를 반환 - * @return boolean - */ - public static function randBool($prob = 0.5) - { - return self::randF() < $prob; - } - - /** - * aaa(.bbb)?% 의 텍스트를 float으로 변환. 100% = 1.0 - * @return float|null - */ - public static function convPercentStrToFloat(string $text):?float{ - preg_match('/^(\d+(\.\d+)?)\%$/', $text, $matches); - if($matches === null){ - return null; - } - - return (float)$matches[1] / 100; - } - - - /** - * $min과 $max 사이의 값으로 교정 - */ - public static function valueFit($value, $min = null, $max = null) - { - if($max !== null && $min !== null && $max < $min){ - return $min; - } - if ($min !== null && $value < $min) { - return $min; - } - if ($max !== null && $value > $max) { - return $max; - } - return $value; - } - - /** - * 각 값의 비중에 따라 랜덤한 값을 선택 - * - * @param array $items 각 수치의 비중 - * - * @return int|string 선택된 랜덤 값의 key값. 단순 배열인 경우에는 index - */ - public static function choiceRandomUsingWeight(array $items) - { - $sum = 0; - foreach ($items as $value) { - if($value <= 0){ - continue; - } - $sum += $value; - } - - $rd = self::randF()*$sum; - foreach ($items as $key=>$value) { - if($value <= 0){ - $value = 0; - } - if ($rd <= $value) { - return $key; - } - $rd -= $value; - } - - //fallback. 이곳으로 빠지지 않음 - end($items); - return key($items); - } - - /** - * 각 값의 비중에 따라 랜덤한 값을 선택. - * - * @param array $items 각 수치와 비중. [값, weight] 으로 보관 - * - * @return object 선택된 랜덤 값의 첫번째 값 - */ - public static function choiceRandomUsingWeightPair(array $items) - { - $sum = 0; - foreach ($items as [$item, $value]) { - if($value <= 0){ - continue; - } - $sum += $value; - } - - $rd = self::randF()*$sum; - foreach ($items as [$item, $value]) { - if($value <= 0){ - $value = 0; - } - if ($rd <= $value) { - return $item; - } - $rd -= $value; - } - - //fallback. 이곳으로 빠지지 않음 - end($items); - return $items[key($items)][0]; - } - - /** - * 2중 배열에서 특정 키의 합 - * - * @param array $array 배열. 1차원 배열 또는 2차원 배열 - * @param int|string|null $key 2차원 배열에서 참조할 키. - * @return int|float 합계 - */ - public static function arraySum(array $array, $key = null){ - if($key === null){ - return array_sum($array); - } - $sum = 0; - foreach($array as $val){ - $sum += $val[$key]; - } - return $sum; - } - - /** - * 특정 키를 가진 값으로 묶음 - * - * @param array $array 배열. 1차원 배열 또는 2차원 배열 - * @param int|string|null $key 2차원 배열에서 참조할 키. - * @return array - */ - public static function arrayGroupBy(array $array, $key, bool $preserveRowKey=false) { - $result = array(); - - if($preserveRowKey){ - foreach($array as $rowKey=>$val) { - if(key_exists($key, $val)){ - $result[$val[$key]][$rowKey] = $val; - }else{ - $result[""][$rowKey] = $val; - } - } - } - else{ - foreach($array as $val) { - if(key_exists($key, $val)){ - $result[$val[$key]][] = $val; - }else{ - $result[""][] = $val; - } - } - } - - return $result; - } - - public static function getKeyOfMaxValue(array $array){ - $max = null; - $result = null; - foreach ($array as $key => $value) { - if ($max === null || $value > $max) { - $result = $key; - $max = $value; - } - } - - return $result; - } - - /** - * 배열의 아무거나 고름. Python의 random.choice() - * - * @param array $items 선택하고자 하는 배열 - * - * @return int|float|string|object 선택된 value값. - */ - public static function choiceRandom(array $items) - { - return $items[array_rand($items)]; - } - - /** - * fqn 클래스 경로에서 클래스 이름을 받아옴 - */ - public static function getClassName(string $classpath) - { - if ($pos = strrpos($classpath, '\\')) return substr($classpath, $pos + 1); - return $pos; - } - - public static function getClassNameFromObj($object){ - $reflect = new \ReflectionClass($object); - return $reflect->getShortName(); - } - - /** - * 배열의 원소에 대해서 테스트를 수행하고 모두 true인지 확인 - */ - public static function testArrayValues(array $array, ?callable $callback):bool{ - if($callback === null){ - foreach($array as $value){ - if(!$value){ - return false; - } - } - return true; - } - - foreach($array as $value){ - if(!($callback)($value)){ - return false; - } - } - return true; - } - - /** - * MeekroDB에서 %lb의 처리가 이상하여 따로 만든 코드 - */ - public static function formatListOfBackticks(array $array):string{ - if(!$array){ - throw new MustNotBeReachedException('backtick 목록에 없음'); - } - //.이 들어간 경우에는 분리해서 묶어야함. - return join(',', array_map(function($value){ - $value = preg_replace('/\s/', '', $value); - if(strpos($value, '.') !== false){ - $value = explode('.', $value); - $value = join('`.`', $value); - } - return "`{$value}`"; - }, $array)); - } - - public static function joinYearMonth(int $year, int $month):int{ - return $year * 12 + $month - 1; - } - - public static function parseYearMonth(int $yearMonth):array{ - return [intdiv($yearMonth, 12), $yearMonth%12 + 1]; - } - - /** - * 변수의 값을 첫번째 값부터 비교해서 대소를 반환 - * 길이가 다른 경우, 앞의 결과를 먼저 비교한 뒤, 짧은쪽의 값을 null으로 가정하여 길이 처리 - */ - public static function arrayCompare(iterable $lhs, iterable $rhs, ?callable $comp=null){ - if($lhs instanceof \Traversable){ - $lhsIter = new \IteratorIterator($lhs); - } - else if(is_array($lhs)){ - $lhsIter = new \ArrayIterator($lhs); - } - else{ - throw new \InvalidArgumentException('$lhs is not Traversable'); - } - - if($rhs instanceof \Traversable){ - $rhsIter = new \IteratorIterator($rhs); - } - else if(is_array($rhs)){ - $rhsIter = new \ArrayIterator($rhs); - } - else{ - throw new \InvalidArgumentException('$rhs is not Traversable'); - } - - while($lhsIter->valid() && $rhsIter->valid()){ - $lhsVal = $lhsIter->current(); - $rhsVal = $rhsIter->current(); - - if($comp !== null){ - $compResult = $comp($lhsVal, $rhsVal); - } - else{ - $compResult = $lhsVal <=> $rhsVal; - } - - if($compResult !== 0){ - return $compResult; - } - - $lhsIter->next(); - $rhsIter->next(); - } - - $rhsVal = null; - while($lhsIter->valid()){ - $lhsVal = $lhsIter->current(); - - if($comp !== null){ - $compResult = $comp($lhsVal, $rhsVal); - } - else{ - $compResult = $lhsVal <=> $rhsVal; - } - - if($compResult !== 0){ - return $compResult; - } - - $lhsIter->next(); - } - - $lhsVal = null; - while($rhsIter->valid()){ - $rhsVal = $rhsIter->current(); - - if($comp !== null){ - $compResult = $comp($lhsVal, $rhsVal); - } - else{ - $compResult = $lhsVal <=> $rhsVal; - } - - if($compResult !== 0){ - return $compResult; - } - - $rhsIter->next(); - } - - return 0; - } - - public static function isPowerOfTwo(int $number):bool{ - if($number <= 0){ - return false; - } - return ($number & ($number - 1)) == 0; - } - - /** - * Python 3의 range와 동일 - * @param int $from - * @param null|int $to - * @param null|int $step - * @return \Traversable - * @throws \InvalidArgumentException - */ - public static function range(int $from, ?int $to=null, ?int $step=null):\Traversable{ - if($to === null){ - $to = $from; - $from = 0; - } - if($step === null){ - $step = 1; - } - else if($step === 0){ - throw new \InvalidArgumentException('xrange() arg 3 must not be zero'); - } - - if($step > 0){ - while($from < $to){ - yield $from; - $from += $step; - } - } - else{ - while($from > $to){ - yield $from; - $from += $step; - } - } - } - -}; +valid()){ + $values[] = $iter->send(NULL); + $hasValue = true; + } + } + yield $values; + + if(!$hasValue){ + break; + } + } + } + + /** + * $_POST, $_GET에서 값을 가져오는 함수. Util::array_get($_POST[$name])을 축약 가능. + * 타입이 복잡해질 경우 이 함수를 통하지 않고 json으로 요청할 것을 권장. + * + * @param string $name 가져오고자 하는 key 이름. + * @param string $type 가져오고자 하는 type. [string, int, float, bool, array, array_string, array_int] + * @param mixed $ifNotExists 만약 $_POST와 $_GET에 값이 없을 경우 반환하는 변수. 이 값은 $type을 검사하지 않음. + * @return int|float|string|array|null + * @throws \InvalidArgumentException + */ + public static function getReq(string $name, string $type = 'string', $ifNotExists = null) + { + if (isset($_POST[$name])) { + $value = $_POST[$name]; + } elseif (isset($_GET[$name])) { + $value = $_GET[$name]; + } else { + return $ifNotExists; + } + + return static::_parseReq($value, $type); + } + + /** + * $_POST에서 값을 가져오는 함수. Util::array_get($_POST[$name])을 축약 가능. $_GET에서도 가져올 수 있다면 getReq 사용. + * 타입이 복잡해질 경우 이 함수를 통하지 않고 json으로 요청할 것을 권장. + * + * @param string $name 가져오고자 하는 key 이름. + * @param string $type 가져오고자 하는 type. [string, int, float, bool, array, array_string, array_int] + * @param mixed $ifNotExists 만약 $_GET과 $_POST에 값이 없을 경우 반환하는 변수. 이 값은 $type을 검사하지 않음. + * @return int|float|string|array|null + * @throws \InvalidArgumentException + */ + public static function getPost(string $name, string $type = 'string', $ifNotExists = null) + { + if (isset($_POST[$name])) { + $value = $_POST[$name]; + } else { + return $ifNotExists; + } + + return static::_parseReq($value, $type); + } + + public static function hashPassword($salt, $password) + { + return hash('sha512', $salt.$password.$salt); + } + + /** + * 변환할 내용이 _tK_$key_ 형태로 작성된 단순한 템플릿 파일을 이용하여 결과물을 생성해주는 함수. + */ + public static function generateFileUsingSimpleTemplate(string $srcFilePath, string $destFilePath, array $params, bool $canOverwrite=false) + { + if ($destFilePath === $srcFilePath) { + return 'invalid destFilePath'; + } + if (!file_exists($srcFilePath)) { + return 'srcFilePath is not exists'; + } + if (file_exists($destFilePath) && !$canOverwrite) { + return 'destFilePath is already exists'; + } + if (!is_writable(dirname($destFilePath))) { + return 'destFilePath is not writable'; + } + + $text = file_get_contents($srcFilePath); + foreach ($params as $key => $value) { + if(is_array($value)){ + $text = str_replace("[/*_tK_{$key}_*/]", var_export($value, true), $text); + } + else{ + $text = str_replace("_tK_{$key}_", $value, $text); + } + + } + file_put_contents($destFilePath, $text); + + return true; + } + + /** + * params에 맞도록 class를 생성해주는 함수 + */ + public static function generatePHPClassFile(string $destFilePath, array $params, ?string $srcClassName=null, string $namespace='sammo'){ + if (!is_writable(dirname($destFilePath))) { + return 'destFilePath is not writable'; + } + + $newClassName = basename($destFilePath, '.php'); + $newClassName = basename($newClassName, '.orig'); + $head = []; + $head[] = '$value){ + + if(!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/',$key)){ + return "$key is not valid variable name"; + } + + $body[] = ' public static $'.$key.' = '.var_export($value, true).';'; + } + $tail = "\n}"; + + if(file_exists($destFilePath)){ + unlink($destFilePath); + } + $result = file_put_contents($destFilePath, $head.join("\n", $body).$tail, LOCK_EX); + assert($result); + + return true; + } + + /** + * '비교적' 안전한 int 변환 + * null -> null + * int -> int + * float -> int + * numeric(int, float) 포함 -> int + * 기타 -> 예외처리 + * + * @return int|null + */ + public static function toInt($val, $silent=false) + { + if (!isset($val)) { + return null; + } + if ($val === null) { + return null; + } + if (is_int($val)) { + return $val; + } + if (is_numeric($val)) { + return intval($val);// + } + if (strtolower($val) === 'null') { + return null; + } + + if ($silent) { + if ($val == null) { + return null; + } + if ($val == ''){ + return null; + } + return intval($val); + } + throw new \InvalidArgumentException('올바르지 않은 타입형 :'.$val); + } + + /** + * Generate a random string, using a cryptographically secure + * pseudorandom number generator (random_int) + * + * For PHP 7, random_int is a PHP core function + * For PHP 5.x, depends on https://github.com/paragonie/random_compat + * + * @param int $length How many characters do we want? + * @param string $keyspace A string of all possible characters + * to select from + * @return string + */ + public static function randomStr($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') + { + $str = ''; + $max = mb_strlen($keyspace, '8bit') - 1; + for ($i = 0; $i < $length; ++$i) { + $str .= $keyspace[random_int(0, $max)]; + } + return $str; + } + + public static function mapWithKey($callback, $dict) + { + $result = []; + foreach ($dict as $key=>$value) { + $result[$key] = ($callback)($key, $value); + } + return $result; + } + + public static function convertArrayToDict($arr, $keyName) + { + $result = []; + + foreach ($arr as $obj) { + $key = $obj[$keyName]; + $result[$key] = $obj; + } + + return $result; + } + + public static function convertArrayToSetLike($arr, $valueIsKey=true){ + $result = []; + foreach($arr as $datum){ + $result[$datum] = $valueIsKey?$datum:1; + } + return $result; + } + + public static function convertPairArrayToDict($arr){ + $result = []; + foreach($arr as [$key, $val]){ + $result[$key] = $val; + } + return $result; + } + + public static function convertDictToArray($dict, bool $withKey=true) + { + $result = []; + + foreach($dict as $key=>$value){ + if($withKey){ + $result[] = [$key, $value]; + } + else{ + $result[] = $value; + } + } + + return $result; + } + + public static function squeezeFromArray(array $dict, $key){ + $result = []; + foreach($dict as $dictKey=>$value){ + $result[$dictKey] = $value[$key]; + } + + return $result; + } + + public static function isDict($array) + { + if($array === null){ + return false; + } + if (!is_array($array)) { + //배열이 아니면 dictionary 조차 아님. + return false; + } + if(count($array) === 0){ + return true; + } + + $idx = 0; + foreach (array_keys($array) as $key) { + if (is_string($key)) { + return true; + } + if($idx !== $key){ + return true; + } + $idx = $key + 1; + } + + return false; + } + + /** + * @param null|mixed|mixed[] $dict + * @return null|mixed|mixed[] + */ + public static function eraseNullValue($dict, int $depth=512) + { + //TODO:Test 추가 + if ($dict === null) { + return null; + } + + if (is_array($dict) && empty($dict)) { + return null; + } + + if ($depth <= 0) { + return $dict; + } + + foreach ($dict as $key=>$value) { + if ($value === null) { + unset($dict[$key]); + continue; + } + if (!Util::isDict($value)) { + continue; + } + + $newValue = Util::eraseNullValue($value, $depth - 1); + if ($newValue === null) { + unset($dict[$key]); + } else { + $dict[$key] = $newValue; + } + } + + return $dict; + } + + /** + * key=>value pair를 보존한 섞기 + */ + public static function shuffle_assoc(&$array) { + $keys = array_keys($array); + + shuffle($keys); + + $new = []; + + foreach($keys as $key) { + $new[$key] = $array[$key]; + } + + $array = $new; + + return true; + } + + + /** + * [0.0, 1.0] 사이의 선형 랜덤 float + * @return float + */ + public static function randF() + { + return mt_rand() / mt_getrandmax(); + } + + /** + * [min, max] 사이의 선형 랜덤 float + * @return float + */ + public static function randRange(float $min, float $max) + { + return static::randF()*($max - $min) + $min; + } + + /** + * [min, max] 사이의 선형 랜덤 int + * 현재는 rand(min, max)와 동일 + * @return int + */ + public static function randRangeInt(int $min, int $max){ + return mt_rand($min, $max); + } + + /** + * $prob의 확률로 true를 반환 + * @return boolean + */ + public static function randBool($prob = 0.5) + { + return self::randF() < $prob; + } + + /** + * aaa(.bbb)?% 의 텍스트를 float으로 변환. 100% = 1.0 + * @return float|null + */ + public static function convPercentStrToFloat(string $text):?float{ + preg_match('/^(\d+(\.\d+)?)\%$/', $text, $matches); + if($matches === null){ + return null; + } + + return (float)$matches[1] / 100; + } + + + /** + * $min과 $max 사이의 값으로 교정 + */ + public static function valueFit($value, $min = null, $max = null) + { + if($max !== null && $min !== null && $max < $min){ + return $min; + } + if ($min !== null && $value < $min) { + return $min; + } + if ($max !== null && $value > $max) { + return $max; + } + return $value; + } + + /** + * 각 값의 비중에 따라 랜덤한 값을 선택 + * + * @param array $items 각 수치의 비중 + * + * @return int|string 선택된 랜덤 값의 key값. 단순 배열인 경우에는 index + */ + public static function choiceRandomUsingWeight(array $items) + { + $sum = 0; + foreach ($items as $value) { + if($value <= 0){ + continue; + } + $sum += $value; + } + + $rd = self::randF()*$sum; + foreach ($items as $key=>$value) { + if($value <= 0){ + $value = 0; + } + if ($rd <= $value) { + return $key; + } + $rd -= $value; + } + + //fallback. 이곳으로 빠지지 않음 + end($items); + return key($items); + } + + /** + * 각 값의 비중에 따라 랜덤한 값을 선택. + * + * @param array $items 각 수치와 비중. [값, weight] 으로 보관 + * + * @return object 선택된 랜덤 값의 첫번째 값 + */ + public static function choiceRandomUsingWeightPair(array $items) + { + $sum = 0; + foreach ($items as [$item, $value]) { + if($value <= 0){ + continue; + } + $sum += $value; + } + + $rd = self::randF()*$sum; + foreach ($items as [$item, $value]) { + if($value <= 0){ + $value = 0; + } + if ($rd <= $value) { + return $item; + } + $rd -= $value; + } + + //fallback. 이곳으로 빠지지 않음 + end($items); + return $items[key($items)][0]; + } + + /** + * 2중 배열에서 특정 키의 합 + * + * @param array $array 배열. 1차원 배열 또는 2차원 배열 + * @param int|string|null $key 2차원 배열에서 참조할 키. + * @return int|float 합계 + */ + public static function arraySum(array $array, $key = null){ + if($key === null){ + return array_sum($array); + } + $sum = 0; + foreach($array as $val){ + $sum += $val[$key]; + } + return $sum; + } + + /** + * 특정 키를 가진 값으로 묶음 + * + * @param array $array 배열. 1차원 배열 또는 2차원 배열 + * @param int|string|null $key 2차원 배열에서 참조할 키. + * @return array + */ + public static function arrayGroupBy(array $array, $key, bool $preserveRowKey=false) { + $result = array(); + + if($preserveRowKey){ + foreach($array as $rowKey=>$val) { + if(key_exists($key, $val)){ + $result[$val[$key]][$rowKey] = $val; + }else{ + $result[""][$rowKey] = $val; + } + } + } + else{ + foreach($array as $val) { + if(key_exists($key, $val)){ + $result[$val[$key]][] = $val; + }else{ + $result[""][] = $val; + } + } + } + + return $result; + } + + public static function getKeyOfMaxValue(array $array){ + $max = null; + $result = null; + foreach ($array as $key => $value) { + if ($max === null || $value > $max) { + $result = $key; + $max = $value; + } + } + + return $result; + } + + /** + * 배열의 아무거나 고름. Python의 random.choice() + * + * @param array $items 선택하고자 하는 배열 + * + * @return int|float|string|object 선택된 value값. + */ + public static function choiceRandom(array $items) + { + return $items[array_rand($items)]; + } + + /** + * fqn 클래스 경로에서 클래스 이름을 받아옴 + */ + public static function getClassName(string $classpath) + { + if ($pos = strrpos($classpath, '\\')) return substr($classpath, $pos + 1); + return $pos; + } + + public static function getClassNameFromObj($object){ + $reflect = new \ReflectionClass($object); + return $reflect->getShortName(); + } + + /** + * 배열의 원소에 대해서 테스트를 수행하고 모두 true인지 확인 + */ + public static function testArrayValues(array $array, ?callable $callback):bool{ + if($callback === null){ + foreach($array as $value){ + if(!$value){ + return false; + } + } + return true; + } + + foreach($array as $value){ + if(!($callback)($value)){ + return false; + } + } + return true; + } + + /** + * MeekroDB에서 %lb의 처리가 이상하여 따로 만든 코드 + */ + public static function formatListOfBackticks(array $array):string{ + if(!$array){ + throw new MustNotBeReachedException('backtick 목록에 없음'); + } + //.이 들어간 경우에는 분리해서 묶어야함. + return join(',', array_map(function($value){ + $value = preg_replace('/\s/', '', $value); + if(strpos($value, '.') !== false){ + $value = explode('.', $value); + $value = join('`.`', $value); + } + return "`{$value}`"; + }, $array)); + } + + public static function joinYearMonth(int $year, int $month):int{ + return $year * 12 + $month - 1; + } + + public static function parseYearMonth(int $yearMonth):array{ + return [intdiv($yearMonth, 12), $yearMonth%12 + 1]; + } + + /** + * 변수의 값을 첫번째 값부터 비교해서 대소를 반환 + * 길이가 다른 경우, 앞의 결과를 먼저 비교한 뒤, 짧은쪽의 값을 null으로 가정하여 길이 처리 + */ + public static function arrayCompare(iterable $lhs, iterable $rhs, ?callable $comp=null){ + if($lhs instanceof \Traversable){ + $lhsIter = new \IteratorIterator($lhs); + } + else if(is_array($lhs)){ + $lhsIter = new \ArrayIterator($lhs); + } + else{ + throw new \InvalidArgumentException('$lhs is not Traversable'); + } + + if($rhs instanceof \Traversable){ + $rhsIter = new \IteratorIterator($rhs); + } + else if(is_array($rhs)){ + $rhsIter = new \ArrayIterator($rhs); + } + else{ + throw new \InvalidArgumentException('$rhs is not Traversable'); + } + + while($lhsIter->valid() && $rhsIter->valid()){ + $lhsVal = $lhsIter->current(); + $rhsVal = $rhsIter->current(); + + if($comp !== null){ + $compResult = $comp($lhsVal, $rhsVal); + } + else{ + $compResult = $lhsVal <=> $rhsVal; + } + + if($compResult !== 0){ + return $compResult; + } + + $lhsIter->next(); + $rhsIter->next(); + } + + $rhsVal = null; + while($lhsIter->valid()){ + $lhsVal = $lhsIter->current(); + + if($comp !== null){ + $compResult = $comp($lhsVal, $rhsVal); + } + else{ + $compResult = $lhsVal <=> $rhsVal; + } + + if($compResult !== 0){ + return $compResult; + } + + $lhsIter->next(); + } + + $lhsVal = null; + while($rhsIter->valid()){ + $rhsVal = $rhsIter->current(); + + if($comp !== null){ + $compResult = $comp($lhsVal, $rhsVal); + } + else{ + $compResult = $lhsVal <=> $rhsVal; + } + + if($compResult !== 0){ + return $compResult; + } + + $rhsIter->next(); + } + + return 0; + } + + public static function isPowerOfTwo(int $number):bool{ + if($number <= 0){ + return false; + } + return ($number & ($number - 1)) == 0; + } + + /** + * Python 3의 range와 동일 + * @param int $from + * @param null|int $to + * @param null|int $step + * @return \Traversable + * @throws \InvalidArgumentException + */ + public static function range(int $from, ?int $to=null, ?int $step=null):\Traversable{ + if($to === null){ + $to = $from; + $from = 0; + } + if($step === null){ + $step = 1; + } + else if($step === 0){ + throw new \InvalidArgumentException('xrange() arg 3 must not be zero'); + } + + if($step > 0){ + while($from < $to){ + yield $from; + $from += $step; + } + } + else{ + while($from > $to){ + yield $from; + $from += $step; + } + } + } + +};