commit 49c9fa4586e1be369b9c1f1148c798754c9623c4 Author: Namhyeon, Go Date: Wed Jun 29 20:45:15 2022 +0900 Create activitiypub.extend.php diff --git a/extend/activitiypub.extend.php b/extend/activitiypub.extend.php new file mode 100644 index 0000000..444d441 --- /dev/null +++ b/extend/activitiypub.extend.php @@ -0,0 +1,419 @@ + + +// References: +// * https://www.w3.org/TR/activitypub/ + +define("ACTIVITYPUB_INSTANCE_ID", md5_file(G5_DATA_PATH . "/dbconfig.php")); +define("ACTIVITYPUB_URL", (empty(G5_URL) ? "http://" . ACTIVITYPUB_INSTANCE_ID . ".local" : G5_URL)); +define("ACTIVITYPUB_DATA_URL", ACTIVITYPUB_URL . '/' . G5_DATA_DIR); +define("ACTIVITYPUB_G5_TABLENAME", G5_TABLE_PREFIX . "apstreams"); +define("ACTIVITYPUB_G5_USERNAME", "apstreams"); +define("NAMESPACE_ACTIVITYSTREAMS", "https://www.w3.org/ns/activitystreams"); +define("NAMESPACE_ACTIVITYSTREAMS_PUBLIC", "https://www.w3.org/ns/activitystreams#Public"); + +function activitypub_get_url($action, $params) { + return ACTIVITYPUB_URL . "/?route=activitypub." . $action . "&" . http_build_query($params); +} + +function activitypub_json_encode($arr) { + return json_encode( $arr ); +} + +function activitypub_get_icon($mb) { + global $config; + + $icon_file_url = ""; + + if ($config['cf_use_member_icon']) { + $mb_dir = substr($mb['mb_id'], 0, 2); + $icon_file = G5_DATA_PATH . '/member/' . $mb_dir . '/' . get_mb_icon_name($mb['mb_id']).'.gif'; + if (file_exists($icon_file)) { + $icon_filemtile = (defined('G5_USE_MEMBER_IMAGE_FILETIME') && G5_USE_MEMBER_IMAGE_FILETIME) ? '?'.filemtime($icon_file) : ''; + $icon_file_url = ACTIVITYPUB_DATA_URL . '/member/' . $mb_dir . '/' . get_mb_icon_name($mb['mb_id']) . '.gif' . $icon_filemtile; + } + } + + if (empty($icon_file_url)) { + $icon_file_url = "https://www.gravatar.com/avatar/" . md5($mb['mb_email']); + } + + return $icon_file_url; +} + +function activitypub_get_recent_items() { + global $g5; + + $items = array(); + $sql = "select * from " . $g5['board_new_table']; + $result = sql_query($sql); + while ($row = sql_fetch_array($result)) { + $sql2 = "select * from " . ($g5['write_prefix'] . $row['bo_table']) . " where wr_id = '" . $row['wr_id'] . "'"; + $row2 = sql_fetch($sql2); + if ($row2['mb_id']) { + array_push($items, array("from" => $row['mb_id'], "to" => $row2['mb_id'])); + } + } + + return $items; +} + +function activitypub_get_followers($mb) { + $followers = array(); + + if ($mb['mb_id']) { + $recent_items = activitypub_get_recent_items(); + foreach($recent_items as $item) { + if ($item['to'] == $mb['mb_id'] && $item['from'] != $mb['mb_id']) { + array_push($followers, $item['from']); + } + } + + $followers = array_unique($followers); + } + + return $followers; +} + +function activitypub_get_following($mb) { + $following = array(); + + if ($mb['mb_id']) { + $recent_items = activitypub_get_recent_items(); + + foreach($recent_items as $item) { + if ($item['from'] == $mb['mb_id'] && $item['to'] != $mb['mb_id']) { + array_push($following, $item['to']); + } + } + + $following = array_unique($following); + } + + return $following; +} + +function activitypub_parse_url($url) { + $ctx = parse_url($object['id']); + $qstr = $ctx['query']; + parse_str($qstr, $qctx); + $ctx['query'] = $qctx; + return $ctx; +} + +function activitypub_add_memo($mb_id, $recv_mb_id, $me_memo) { + $tmp_row = sql_fetch(" select max(me_id) as max_me_id from {$g5['memo_table']} "); + $me_id = $tmp_row['max_me_id'] + 1; + + $sql = " + insert into {$g5['memo_table']} ( + me_recv_mb_id, + me_send_mb_id, + me_send_datetime + me_memo, + me_read_datetime, + me_type, + me_send_ip + ) values ( + '$recv_mb_id', + '$mb_id', + '".G5_TIME_YMDHIS."', + '{$me_memo}', + '0000-00-00 00:00:00' , + 'recv', + '{$_SERVER['REMOTE_ADDR']}' + ) + "; + sql_query($sql); + + return ($me_id == sql_insert_id()); +} + +class _GNUBOARD_ActivityPub { + public static function open() { + header("Content-Type: application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""); + } + + public static function whois() { + $mb = get_member($_GET['mb_id']); + + if (!$mb['mb_id']) { + return activitypub_json_encode(array("message" => "Could not find the user")); + } + + $context = array( + "@context" => array(ACTIVITYPUB_CONTEXT_URL, array("@language" => "ko")), + "type" => "Person", + "id" => activitypub_get_url("whois", array("mb_id" => $mb['mb_id'])), + "name" => $mb['mb_name'], + "preferredUsername" => $mb['mb_nick'], + "summary" => $mb['mb_profile'], + "inbox" => activitypub_get_url("inbox", array("mb_id" => $mb['mb_id'])), + "outbox" => activitypub_get_url("outbox", array("mb_id" => $mb['mb_id'])), + "followers" => activitypub_get_url("followers", array("mb_id" => $mb['mb_id'])), + "following" => activitypub_get_url("following", array("mb_id" => $mb['mb_id'])), + "liked" => activitypub_get_url("liked", array("mb_id" => $mb['mb_id'])), + "icon" => array( + activitypub_get_icon($mb) + ) + ); + + return activitypub_json_encode($context); + } + + public static function streams() { + $params = array( + "bo_table" => $_GET['bo_table'], + "wr_id" => $_GET['wr_id'] + ); + + if (!empty($params['bo_table']) && !empty($params['wr_id'])) { + $query_string = http_build_query(array( + "bo_table" => $params['bo_table'], + "wr_id" => $params['wr_id'] + )); + + $link = G5_BBS_URL . "/board.php?" . $query_string; + header("Location: " . $link); + } else { + return activitypub_json_encode(array("message" => "Could not find the stream")); + } + } + + public static function inbox() { + // 개인에게 보낸 메시지는 쪽지 + // 공개(Public) 설정한 메시지는 ACTIVITYPUB_STREAMS_TABLENAME 에 저장 + + $data = json_decode(file_get_contents("php://input"), true); + + if (empty($data['@context'])) { + return activitypub_json_encode(array("message" => "This is a broken context")); + } + + if ($data['@context'] != NAMESPACE_ACTIVITYSTREAMS) { + return activitypub_json_encode(array("message" => "This is not an ActivityStreams request")); + } + + switch ($data['type']) { + case "Create": + // 멤버정보 처리 + $mb_id = activitypub_parse_url($data['actor'])['query']['mb_id']; + if (empty($mb_id)) { + return activitypub_json_encode(array("message" => "This is not a valid actor")); + } + $mb = get_member($mb_id); + + // 내용 처리 + $object = $data['object']; + if (empty($object)) { + return activitypub_json_encode(array("message" => "Content is empty")); + } + $content = $object['content']; + + // 받을사람 처리 + foreach($to as $_to) { + $query = activitypub_parse_url($_to)['query']; + + // 공개 게시물일 때 + if ($_to == NAMESPACE_ACTIVITYSTREAMS_PUBLIC) { + $mb = get_member(ACTIVITYPUB_G5_USERNAME); + + $write_table = ACTIVITYPUB_G5_TABLENAME; + $wr_num = get_next_num($write_table); + $wr_reply = ''; + $ca_name = 'ActivityStreams'; + $wr_subject = mb_substr($content, 0, 45); + $wr_content = $content; + $wr_link1 = $data['actor']; + $wr_homepage = $data['actor']; + + $sql = " + insert into $write_table + set wr_num = '$wr_num', + wr_reply = '$wr_reply', + wr_comment = 0, + ca_name = '$ca_name', + wr_option = '', + wr_subject = '$wr_subject', + wr_content = '$wr_content', + wr_seo_title = '$wr_seo_title', + wr_link1 = '$wr_link1', + wr_link2 = '', + wr_link1_hit = 0, + wr_link2_hit = 0, + wr_hit = 0, + wr_good = 0, + wr_nogood = 0, + mb_id = '{$mb['mb_id']}', + wr_password = '', + wr_name = '{$mb['mb_name']}', + wr_email = '', + wr_homepage = '$wr_homepage', + wr_datetime = '" . G5_TIME_YMDHIS . "', + wr_last = '" . G5_TIME_YMDHIS . "', + wr_ip = '{$_SERVER['REMOTE_ADDR']}', + wr_1 = '', + wr_2 = '', + wr_3 = '', + wr_4 = '', + wr_5 = '', + wr_6 = '', + wr_7 = '', + wr_8 = '', + wr_9 = '', + wr_10 = '' + "; + sql_query($sql); + + return activitypub_json_encode(array("message" => "Success")); + } + + // 특정 회원이 지목되어 있을 때 -> 메모로 작성 + else if (!empty($query['mb_id'])) { + switch ($query['route']) { + case "activitypub.whois": + activitypub_add_memo($query['mb_id'], $content); + break; + + case "activitypub.followers": + $followers = activitypub_get_followers($mb); + foreach($followers as $_mb_id) { + activitypub_add_memo($_mb_id, $content); + } + break; + + case "activitypub.following": + $following = activitypub_get_following($mb); + foreach($following as $_mb_id) { + activitypub_add_memo($_mb_id, $content); + } + break; + } + } + + // 특정 글이 지목되어 있을 때 -> 댓글로 작성 + else if (!empty($query['bo_table']) && !empty($query['wr_id'])) { + $wr_id = $query['wr_id']; + $write_table = G5_TABLE_PREFIX . $query['bo_table']; + $wr = get_write($write_table, $wr_id); + + if (!empty($wr['wr_id'])) { + $mb = get_member(ACTIVITYPUB_G5_USERNAME); + $wr_homepage = $data['actor']; + + $sql = " + insert into $write_table + set ca_name = '{$wr['ca_name']}', + wr_option = '', + wr_num = '{$wr['wr_num']}', + wr_reply = '', + wr_parent = '{$wr['wr_id']}', + wr_is_comment = 1, + wr_comment = '', + wr_comment_reply = '', + wr_subject = '', + wr_content = '$content', + mb_id = '{$mb['mb_id']}', + wr_password = '', + wr_name = '{$mb['mb_name']}', + wr_email = '', + wr_homepage = '$wr_homepage', + wr_datetime = '" . G5_TIME_YMDHIS . "', + wr_last = '', + wr_ip = '{$_SERVER['REMOTE_ADDR']}', + wr_1 = '', + wr_2 = '', + wr_3 = '', + wr_4 = '', + wr_5 = '', + wr_6 = '', + wr_7 = '', + wr_8 = '', + wr_9 = '', + wr_10 = '' + "; + sql_query($sql); + } + } + } + break; + + default: + return activitypub_json_encode(array("message" => "This is not implemented type")); + } + } + + public static function outbox() { + // TODO + } + + public static function followers() { + $mb = get_member($_GET['mb_id']); + return activitypub_json_encode(array("followers" => activitypub_get_followers($mb))); + } + + public static function following() { + $mb = get_member($_GET['mb_id']); + return activitypub_json_encode(array("following" => activitypub_get_following($mb))); + } + + public static function liked() { + return array( + "message" => "Not implemented" + ); + } + + public static function close() { + exit(); + } +} + +$route = $_GET['route']; + +switch ($route) { + case "activitypub.whois": + _GNUBOARD_ActivityPub::open(); + echo _GNUBOARD_ActivityPub::whois(); + _GNUBOARD_ActivityPub::close(); + break; + + case "activitypub.streams": + _GNUBOARD_ActivityPub::open(); + echo _GNUBOARD_ActivityPub::streams(); + _GNUBOARD_ActivityPub::close(); + break; + + case "activitypub.inbox": + _GNUBOARD_ActivityPub::open(); + echo _GNUBOARD_ActivityPub::inbox(); + _GNUBOARD_ActivityPub::close(); + break; + + case "activitypub.outbox": + _GNUBOARD_ActivityPub::open(); + echo _GNUBOARD_ActivityPub::outbox(); + _GNUBOARD_ActivityPub::close(); + break; + + case "activitypub.followers": + _GNUBOARD_ActivityPub::open(); + echo _GNUBOARD_ActivityPub::followers(); + _GNUBOARD_ActivityPub::close(); + break; + + case "activitypub.following": + _GNUBOARD_ActivityPub::open(); + echo _GNUBOARD_ActivityPub::following(); + _GNUBOARD_ActivityPub::close(); + break; + + case "activitypub.liked": + _GNUBOARD_ActivityPub::open(); + echo _GNUBOARD_ActivityPub::liked(); + _GNUBOARD_ActivityPub::close(); + break; +} +