From 8cb8c9242c91d0cbc32cd40bff346ed6d66b1e74 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 1 Jun 2018 00:42:37 +0900
Subject: [PATCH] wip

---
 src/models/user.ts                            | 13 ++++++-
 src/remote/activitypub/kernel/accept/index.ts |  2 +-
 src/remote/activitypub/kernel/index.ts        |  8 ++++-
 .../activitypub/kernel/reject/follow.ts       | 27 ++++++++++++++
 src/remote/activitypub/kernel/reject/index.ts | 35 +++++++++++++++++++
 src/remote/activitypub/renderer/reject.ts     |  4 +++
 src/remote/activitypub/type.ts                |  5 +++
 .../api/endpoints/users/recommendation.ts     |  1 +
 src/services/user/reject-follow-request.ts    | 18 ++++++++++
 9 files changed, 110 insertions(+), 3 deletions(-)
 create mode 100644 src/remote/activitypub/kernel/reject/follow.ts
 create mode 100644 src/remote/activitypub/kernel/reject/index.ts
 create mode 100644 src/remote/activitypub/renderer/reject.ts
 create mode 100644 src/services/user/reject-follow-request.ts

diff --git a/src/models/user.ts b/src/models/user.ts
index aa051517f..4186241a5 100644
--- a/src/models/user.ts
+++ b/src/models/user.ts
@@ -416,7 +416,7 @@ export const pack = (
 	}
 
 	if (meId && !meId.equals(_user.id)) {
-		const [following1, following2, mute] = await Promise.all([
+		const [following1, following2, followReq1, followReq2, mute] = await Promise.all([
 			Following.findOne({
 				followerId: meId,
 				followeeId: _user.id
@@ -425,6 +425,14 @@ export const pack = (
 				followerId: _user.id,
 				followeeId: meId
 			}),
+			_user.isLocked ? FollowRequest.findOne({
+				followerId: meId,
+				followeeId: _user.id
+			}) : Promise.resolve(null),
+			FollowRequest.findOne({
+				followerId: _user.id,
+				followeeId: meId
+			}),
 			Mute.findOne({
 				muterId: meId,
 				muteeId: _user.id
@@ -435,6 +443,9 @@ export const pack = (
 		_user.isFollowing = following1 !== null;
 		_user.isStalking = following1 && following1.stalk;
 
+		_user.hasPendingFollowRequestFromYou = followReq1 !== null;
+		_user.hasPendingFollowRequestToYou = followReq2 !== null;
+
 		// Whether the user is followed
 		_user.isFollowed = following2 !== null;
 
diff --git a/src/remote/activitypub/kernel/accept/index.ts b/src/remote/activitypub/kernel/accept/index.ts
index b647df022..2f9d646d1 100644
--- a/src/remote/activitypub/kernel/accept/index.ts
+++ b/src/remote/activitypub/kernel/accept/index.ts
@@ -25,7 +25,7 @@ export default async (actor: IRemoteUser, activity: IAccept): Promise<void> => {
 
 	switch (object.type) {
 	case 'Follow':
-	acceptFollow(resolver, actor, activity, object);
+		acceptFollow(actor, object);
 		break;
 
 	default:
diff --git a/src/remote/activitypub/kernel/index.ts b/src/remote/activitypub/kernel/index.ts
index 15ea9494a..752a9bd2e 100644
--- a/src/remote/activitypub/kernel/index.ts
+++ b/src/remote/activitypub/kernel/index.ts
@@ -6,6 +6,8 @@ import follow from './follow';
 import undo from './undo';
 import like from './like';
 import announce from './announce';
+import accept from './accept';
+import reject from './reject';
 
 const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
 	switch (activity.type) {
@@ -22,7 +24,11 @@ const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
 		break;
 
 	case 'Accept':
-		// noop
+		await accept(actor, activity);
+		break;
+
+	case 'Reject':
+		await reject(actor, activity);
 		break;
 
 	case 'Announce':
diff --git a/src/remote/activitypub/kernel/reject/follow.ts b/src/remote/activitypub/kernel/reject/follow.ts
new file mode 100644
index 000000000..862f3def3
--- /dev/null
+++ b/src/remote/activitypub/kernel/reject/follow.ts
@@ -0,0 +1,27 @@
+import * as mongo from 'mongodb';
+import User, { IRemoteUser } from '../../../../models/user';
+import config from '../../../../config';
+import reject from '../../../../services/user/reject-follow-request';
+import { IFollow } from '../../type';
+
+export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => {
+	const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
+
+	if (!id.startsWith(config.url + '/')) {
+		return null;
+	}
+
+	const follower = await User.findOne({
+		_id: new mongo.ObjectID(id.split('/').pop())
+	});
+
+	if (follower === null) {
+		throw new Error('follower not found');
+	}
+
+	if (follower.host != null) {
+		throw new Error('フォローリクエストしたユーザーはローカルユーザーではありません');
+	}
+
+	await reject(actor, follower);
+};
diff --git a/src/remote/activitypub/kernel/reject/index.ts b/src/remote/activitypub/kernel/reject/index.ts
new file mode 100644
index 000000000..a82c3fd61
--- /dev/null
+++ b/src/remote/activitypub/kernel/reject/index.ts
@@ -0,0 +1,35 @@
+import * as debug from 'debug';
+
+import Resolver from '../../resolver';
+import { IRemoteUser } from '../../../../models/user';
+import rejectFollow from './follow';
+import { IReject } from '../../type';
+
+const log = debug('misskey:activitypub');
+
+export default async (actor: IRemoteUser, activity: IReject): Promise<void> => {
+	const uri = activity.id || activity;
+
+	log(`Reject: ${uri}`);
+
+	const resolver = new Resolver();
+
+	let object;
+
+	try {
+		object = await resolver.resolve(activity.object);
+	} catch (e) {
+		log(`Resolution failed: ${e}`);
+		throw e;
+	}
+
+	switch (object.type) {
+	case 'Follow':
+		rejectFollow(actor, object);
+		break;
+
+	default:
+		console.warn(`Unknown reject type: ${object.type}`);
+		break;
+	}
+};
diff --git a/src/remote/activitypub/renderer/reject.ts b/src/remote/activitypub/renderer/reject.ts
new file mode 100644
index 000000000..29c998a6b
--- /dev/null
+++ b/src/remote/activitypub/renderer/reject.ts
@@ -0,0 +1,4 @@
+export default object => ({
+	type: 'Reject',
+	object
+});
diff --git a/src/remote/activitypub/type.ts b/src/remote/activitypub/type.ts
index 77e6bc304..13e1969b4 100644
--- a/src/remote/activitypub/type.ts
+++ b/src/remote/activitypub/type.ts
@@ -83,6 +83,10 @@ export interface IAccept extends IActivity {
 	type: 'Accept';
 }
 
+export interface IReject extends IActivity {
+	type: 'Reject';
+}
+
 export interface ILike extends IActivity {
 	type: 'Like';
 	_misskey_reaction: string;
@@ -100,5 +104,6 @@ export type Object =
 	IUndo |
 	IFollow |
 	IAccept |
+	IReject |
 	ILike |
 	IAnnounce;
diff --git a/src/server/api/endpoints/users/recommendation.ts b/src/server/api/endpoints/users/recommendation.ts
index 620ae17ca..23821a552 100644
--- a/src/server/api/endpoints/users/recommendation.ts
+++ b/src/server/api/endpoints/users/recommendation.ts
@@ -36,6 +36,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
 			_id: {
 				$nin: followingIds.concat(mutedUserIds)
 			},
+			isLocked: false,
 			$or: [{
 				lastUsedAt: {
 					$gte: new Date(Date.now() - ms('7days'))
diff --git a/src/services/user/reject-follow-request.ts b/src/services/user/reject-follow-request.ts
new file mode 100644
index 000000000..32965a155
--- /dev/null
+++ b/src/services/user/reject-follow-request.ts
@@ -0,0 +1,18 @@
+import User, { IUser, isRemoteUser, ILocalUser } from "../../models/user";
+import FollowRequest from "../../models/follow-request";
+import pack from '../../remote/activitypub/renderer';
+import renderFollow from '../../remote/activitypub/renderer/follow';
+import renderReject from '../../remote/activitypub/renderer/reject';
+import { deliver } from '../../queue';
+
+export default async function(followee: IUser, follower: IUser) {
+	if (isRemoteUser(follower)) {
+		const content = pack(renderReject(renderFollow(follower, followee)));
+		deliver(followee as ILocalUser, content, follower.inbox);
+	}
+
+	FollowRequest.remove({
+		followeeId: followee._id,
+		followerId: follower._id
+	});
+}