allow fileserverv2 attachments to be downloaded, upload disabled

This commit is contained in:
Audric Ackermann 2021-05-06 14:02:49 +10:00
parent 64eab5160d
commit 2b576de2cd
No known key found for this signature in database
GPG key ID: 999F434D76324AD4
11 changed files with 412 additions and 275 deletions

View file

@ -0,0 +1,131 @@
import { OpenGroupV2Request } from '../opengroup/opengroupV2/ApiUtil';
import { sendApiV2Request } from '../opengroup/opengroupV2/OpenGroupAPIV2';
import { parseStatusCodeFromOnionRequest } from '../opengroup/opengroupV2/OpenGroupAPIV2Parser';
import { fromArrayBufferToBase64, fromBase64ToArrayBuffer } from '../session/utils/String';
// tslint:disable-next-line: no-http-string
export const fileServerV2URL = 'http://88.99.175.227';
export const fileServerV2PubKey =
'7cb31905b55cd5580c686911debf672577b3fb0bff81df4ce2d5c4cb3a7aaa69';
export type FileServerV2Request = {
method: 'GET' | 'POST' | 'DELETE' | 'PUT';
endpoint: string;
// queryParams are used for post or get, but not the same way
queryParams?: Record<string, any>;
headers?: Record<string, string>;
};
const FILES_ENDPOINT = 'files';
// Disable this if you don't want to use the file server v2 for sending
// Receiving is always enabled if the attachments url matches a fsv2 url
export const useFileServerAPIV2Sending = false;
/**
* Upload a file to the file server v2
* @param fileContent the data to send
* @returns null or the fileID and complete URL to share this file
*/
export const uploadFileToFsV2 = async (
fileContent: ArrayBuffer
): Promise<{ fileId: number; fileUrl: string } | null> => {
if (!fileContent || !fileContent.byteLength) {
return null;
}
const queryParams = {
file: fromArrayBufferToBase64(fileContent),
};
const request: FileServerV2Request = {
method: 'POST',
endpoint: FILES_ENDPOINT,
queryParams,
};
const result = await sendApiV2Request(request);
const statusCode = parseStatusCodeFromOnionRequest(result);
if (statusCode !== 200) {
return null;
}
// we should probably change the logic of sendOnionRequest to not have all those levels
const fileId = (result as any)?.result?.result as number | undefined;
if (!fileId) {
return null;
}
const fileUrl = `${fileServerV2URL}/${FILES_ENDPOINT}/${fileId}`;
return {
fileId: fileId,
fileUrl,
};
};
/**
* Download a file given the fileId from the fileserver v2
* @param fileId the fileId to download
* @returns the data as an Uint8Array or null
*/
export const downloadFileFromFSv2 = async (fileId: string): Promise<ArrayBuffer | null> => {
if (!fileId) {
window.log.warn('');
return null;
}
const request: FileServerV2Request = {
method: 'GET',
endpoint: `${FILES_ENDPOINT}/${fileId}`,
};
const result = await sendApiV2Request(request);
const statusCode = parseStatusCodeFromOnionRequest(result);
if (statusCode !== 200) {
return null;
}
// we should probably change the logic of sendOnionRequest to not have all those levels
const base64Data = (result as any)?.result?.result as string | undefined;
if (!base64Data) {
return null;
}
return fromBase64ToArrayBuffer(base64Data);
};
/**
* This is a typescript type guard
* request.isAuthRequired Must be set for an OpenGroupV2Request
* @returns true if request.isAuthRequired is not undefined
*/
export function isOpenGroupV2Request(
request: FileServerV2Request | OpenGroupV2Request
): request is OpenGroupV2Request {
return (request as OpenGroupV2Request).isAuthRequired !== undefined;
}
/**
* Try to build an full url and check it for validity.
* @returns null if the check failed. the built URL otherwise
*/
export const buildUrl = (request: FileServerV2Request | OpenGroupV2Request): URL | null => {
let rawURL: string;
if (isOpenGroupV2Request(request)) {
rawURL = `${request.server}/${request.endpoint}`;
} else {
rawURL = `${fileServerV2URL}/${request.endpoint}`;
}
if (request.method === 'GET') {
const entries = Object.entries(request.queryParams || {});
if (entries.length) {
const queryString = entries.map(([key, value]) => `${key}=${value}`).join('&');
rawURL += `?${queryString}`;
}
}
// this just check that the URL is valid
try {
return new URL(`${rawURL}`);
} catch (error) {
return null;
}
};

4
ts/fileserver/index.ts Normal file
View file

@ -0,0 +1,4 @@
import * as FSv2 from './FileServerApiV2';
// fsv2 = File server V2
export { FSv2 };

View file

@ -0,0 +1,161 @@
import { getV2OpenGroupRoomByRoomId, saveV2OpenGroupRoom } from '../../data/opengroups';
import { allowOnlyOneAtATime } from '../../session/utils/Promise';
import { fromBase64ToArrayBuffer, toHex } from '../../session/utils/String';
import { getIdentityKeyPair, getOurPubKeyStrFromCache } from '../../session/utils/User';
import { OpenGroupRequestCommonType, OpenGroupV2Request } from './ApiUtil';
import { sendApiV2Request } from './OpenGroupAPIV2';
import { parseStatusCodeFromOnionRequest } from './OpenGroupAPIV2Parser';
async function claimAuthToken(
authToken: string,
serverUrl: string,
roomId: string
): Promise<string | null> {
// Set explicitly here because is isn't in the database yet at this point
const headers = { Authorization: authToken };
const request: OpenGroupV2Request = {
method: 'POST',
headers,
room: roomId,
server: serverUrl,
queryParams: { public_key: getOurPubKeyStrFromCache() },
isAuthRequired: false,
endpoint: 'claim_auth_token',
};
const result = await sendApiV2Request(request);
const statusCode = parseStatusCodeFromOnionRequest(result);
if (statusCode !== 200) {
window.log.warn(`Could not claim token, status code: ${statusCode}`);
return null;
}
return authToken;
}
export async function getAuthToken({
serverUrl,
roomId,
}: OpenGroupRequestCommonType): Promise<string | null> {
// first try to fetch from db a saved token.
const roomDetails = await getV2OpenGroupRoomByRoomId({ serverUrl, roomId });
if (!roomDetails) {
window.log.warn('getAuthToken Room does not exist.');
return null;
}
if (roomDetails?.token) {
return roomDetails.token;
}
await allowOnlyOneAtATime(`getAuthTokenV2${serverUrl}:${roomId}`, async () => {
try {
window.log.info('TRIGGERING NEW AUTH TOKEN WITH', { serverUrl, roomId });
const token = await requestNewAuthToken({ serverUrl, roomId });
if (!token) {
window.log.warn('invalid new auth token', token);
return;
}
const claimedToken = await claimAuthToken(token, serverUrl, roomId);
if (!claimedToken) {
window.log.warn('invalid claimed token', claimedToken);
}
// still save it to the db. just to mark it as to be refreshed later
roomDetails.token = claimedToken || '';
await saveV2OpenGroupRoom(roomDetails);
} catch (e) {
window.log.error('Failed to getAuthToken', e);
throw e;
}
});
const refreshedRoomDetails = await getV2OpenGroupRoomByRoomId({
serverUrl,
roomId,
});
if (!refreshedRoomDetails) {
window.log.warn('getAuthToken Room does not exist.');
return null;
}
if (refreshedRoomDetails?.token) {
return refreshedRoomDetails?.token;
}
return null;
}
export const deleteAuthToken = async ({
serverUrl,
roomId,
}: OpenGroupRequestCommonType): Promise<boolean> => {
const request: OpenGroupV2Request = {
method: 'DELETE',
room: roomId,
server: serverUrl,
isAuthRequired: false,
endpoint: 'auth_token',
};
try {
const result = await sendApiV2Request(request);
const statusCode = parseStatusCodeFromOnionRequest(result);
if (statusCode !== 200) {
window.log.warn(`Could not deleteAuthToken, status code: ${statusCode}`);
return false;
}
return true;
} catch (e) {
window.log.error('deleteAuthToken failed:', e);
return false;
}
};
// tslint:disable: member-ordering
export async function requestNewAuthToken({
serverUrl,
roomId,
}: OpenGroupRequestCommonType): Promise<string | null> {
const userKeyPair = await getIdentityKeyPair();
if (!userKeyPair) {
throw new Error('Failed to fetch user keypair');
}
const ourPubkey = getOurPubKeyStrFromCache();
const parameters = {} as Record<string, string>;
parameters.public_key = ourPubkey;
const request: OpenGroupV2Request = {
method: 'GET',
room: roomId,
server: serverUrl,
queryParams: parameters,
isAuthRequired: false,
endpoint: 'auth_token_challenge',
};
const json = (await sendApiV2Request(request)) as any;
// parse the json
if (!json || !json?.result?.challenge) {
window.log.warn('Parsing failed');
return null;
}
const {
ciphertext: base64EncodedCiphertext,
ephemeral_public_key: base64EncodedEphemeralPublicKey,
} = json?.result?.challenge;
if (!base64EncodedCiphertext || !base64EncodedEphemeralPublicKey) {
window.log.warn('Parsing failed');
return null;
}
const ciphertext = fromBase64ToArrayBuffer(base64EncodedCiphertext);
const ephemeralPublicKey = fromBase64ToArrayBuffer(base64EncodedEphemeralPublicKey);
try {
const symmetricKey = await window.libloki.crypto.deriveSymmetricKey(
ephemeralPublicKey,
userKeyPair.privKey
);
const plaintextBuffer = await window.libloki.crypto.DecryptAESGCM(symmetricKey, ciphertext);
const token = toHex(plaintextBuffer);
return token;
} catch (e) {
window.log.error('Failed to decrypt token open group v2');
return null;
}
}

View file

@ -1,4 +1,5 @@
import _ from 'underscore';
import { FileServerV2Request } from '../../fileserver/FileServerApiV2';
import { PubKey } from '../../session/types';
import { allowOnlyOneAtATime } from '../../session/utils/Promise';
import { fromBase64ToArrayBuffer, fromHex } from '../../session/utils/String';
@ -13,14 +14,9 @@ export type OpenGroupRequestCommonType = {
roomId: string;
};
export type OpenGroupV2Request = {
method: 'GET' | 'POST' | 'DELETE' | 'PUT';
export type OpenGroupV2Request = FileServerV2Request & {
room: string;
server: string;
endpoint: string;
// queryParams are used for post or get, but not the same way
queryParams?: Record<string, any>;
headers?: Record<string, string>;
isAuthRequired: boolean;
serverPublicKey?: string; // if not provided, a db called will be made to try to get it.
};
@ -43,28 +39,6 @@ export type OpenGroupV2InfoJoinable = OpenGroupV2Info & {
base64Data?: string;
};
/**
* Try to build an full url and check it for validity.
* @returns null if the check failed. the built URL otherwise
*/
export const buildUrl = (request: OpenGroupV2Request): URL | null => {
let rawURL = `${request.server}/${request.endpoint}`;
if (request.method === 'GET') {
const entries = Object.entries(request.queryParams || {});
if (entries.length) {
const queryString = entries.map(([key, value]) => `${key}=${value}`).join('&');
rawURL += `?${queryString}`;
}
}
// this just check that the URL is valid
try {
return new URL(`${rawURL}`);
} catch (error) {
return null;
}
};
export const parseMessages = async (
rawMessages: Array<Record<string, any>>
): Promise<Array<OpenGroupMessageV2>> => {

View file

@ -4,24 +4,11 @@ import {
OpenGroupV2Room,
saveV2OpenGroupRoom,
} from '../../data/opengroups';
import { ConversationController } from '../../session/conversations';
import { FSv2 } from '../../fileserver/';
import { sendViaOnion } from '../../session/onions/onionSend';
import { PubKey } from '../../session/types';
import { allowOnlyOneAtATime } from '../../session/utils/Promise';
import {
fromArrayBufferToBase64,
fromBase64ToArrayBuffer,
toHex,
} from '../../session/utils/String';
import { getIdentityKeyPair, getOurPubKeyStrFromCache } from '../../session/utils/User';
import { getCompleteEndpointUrl, getOpenGroupV2ConversationId } from '../utils/OpenGroupUtils';
import {
buildUrl,
OpenGroupRequestCommonType,
OpenGroupV2Info,
OpenGroupV2Request,
parseMessages,
} from './ApiUtil';
import { fromArrayBufferToBase64, fromBase64ToArrayBuffer } from '../../session/utils/String';
import { OpenGroupRequestCommonType, OpenGroupV2Info, OpenGroupV2Request } from './ApiUtil';
import {
parseMemberCount,
parseRooms,
@ -29,13 +16,63 @@ import {
} from './OpenGroupAPIV2Parser';
import { OpenGroupMessageV2 } from './OpenGroupMessageV2';
import { isOpenGroupV2Request } from '../../fileserver/FileServerApiV2';
import { getAuthToken } from './ApiAuth';
/**
* This send function is to be used for all non polling stuff
* download and upload of attachments for instance, but most of the logic happens in
* the compact_poll endpoint
* This function returns a base url to this room
* This is basically used for building url after posting an attachment
* hasRoomInEndpoint = true means the roomId is already in the endpoint.
* so we don't add the room after the serverUrl.
*
*/
async function sendOpenGroupV2Request(request: OpenGroupV2Request): Promise<Object | null> {
const builtUrl = buildUrl(request);
function getCompleteEndpointUrl(
roomInfos: OpenGroupRequestCommonType,
endpoint: string,
hasRoomInEndpoint: boolean
) {
// serverUrl has the port and protocol already
if (!hasRoomInEndpoint) {
return `${roomInfos.serverUrl}/${roomInfos.roomId}/${endpoint}`;
}
// not room based, the endpoint already has the room in it
return `${roomInfos.serverUrl}/${endpoint}`;
}
const getDestinationPubKey = async (
request: OpenGroupV2Request | FSv2.FileServerV2Request
): Promise<string> => {
if (FSv2.isOpenGroupV2Request(request)) {
if (!request.serverPublicKey) {
const roomDetails = await getV2OpenGroupRoomByRoomId({
serverUrl: request.server,
roomId: request.room,
});
if (!roomDetails?.serverPublicKey) {
throw new Error('PublicKey not found for this server.');
}
return roomDetails.serverPublicKey;
} else {
return request.serverPublicKey;
}
} else {
// this is a fileServer call
return FSv2.fileServerV2PubKey;
}
};
/**
*
* This send function is to be used for all non polling stuff.
* This function can be used for OpengroupV2 request OR File Server V2 request
* Download and upload of attachments for instance, but most of the logic happens in
* the compact_poll endpoint.
*
*/
export async function sendApiV2Request(
request: OpenGroupV2Request | FSv2.FileServerV2Request
): Promise<Object | null> {
const builtUrl = FSv2.buildUrl(request);
if (!builtUrl) {
throw new Error('Invalid request');
@ -43,28 +80,19 @@ async function sendOpenGroupV2Request(request: OpenGroupV2Request): Promise<Obje
// set the headers sent by the caller, and the roomId.
const headers = request.headers || {};
headers.Room = request.room;
if (FSv2.isOpenGroupV2Request(request)) {
headers.Room = request.room;
}
let body = '';
if (request.method !== 'GET') {
body = JSON.stringify(request.queryParams);
}
let destinationX25519Key: string;
if (!request.serverPublicKey) {
const roomDetails = await getV2OpenGroupRoomByRoomId({
serverUrl: request.server,
roomId: request.room,
});
if (!roomDetails?.serverPublicKey) {
throw new Error('PublicKey not found for this server.');
}
destinationX25519Key = roomDetails.serverPublicKey;
} else {
destinationX25519Key = request.serverPublicKey;
}
const destinationX25519Key = await getDestinationPubKey(request);
// Because auth happens on a per-room basis, we need both to make an authenticated request
if (request.isAuthRequired && request.room) {
if (isOpenGroupV2Request(request) && request.isAuthRequired && request.room) {
// this call will either return the token on the db,
// or the promise currently fetching a new token for that same room
// or fetch from the open group a new token for that room.
@ -122,61 +150,6 @@ async function sendOpenGroupV2Request(request: OpenGroupV2Request): Promise<Obje
}
}
// tslint:disable: member-ordering
export async function requestNewAuthToken({
serverUrl,
roomId,
}: OpenGroupRequestCommonType): Promise<string | null> {
const userKeyPair = await getIdentityKeyPair();
if (!userKeyPair) {
throw new Error('Failed to fetch user keypair');
}
const ourPubkey = getOurPubKeyStrFromCache();
const parameters = {} as Record<string, string>;
parameters.public_key = ourPubkey;
const request: OpenGroupV2Request = {
method: 'GET',
room: roomId,
server: serverUrl,
queryParams: parameters,
isAuthRequired: false,
endpoint: 'auth_token_challenge',
};
const json = (await sendOpenGroupV2Request(request)) as any;
// parse the json
if (!json || !json?.result?.challenge) {
window.log.warn('Parsing failed');
return null;
}
const {
ciphertext: base64EncodedCiphertext,
ephemeral_public_key: base64EncodedEphemeralPublicKey,
} = json?.result?.challenge;
if (!base64EncodedCiphertext || !base64EncodedEphemeralPublicKey) {
window.log.warn('Parsing failed');
return null;
}
const ciphertext = fromBase64ToArrayBuffer(base64EncodedCiphertext);
const ephemeralPublicKey = fromBase64ToArrayBuffer(base64EncodedEphemeralPublicKey);
try {
const symmetricKey = await window.libloki.crypto.deriveSymmetricKey(
ephemeralPublicKey,
userKeyPair.privKey
);
const plaintextBuffer = await window.libloki.crypto.DecryptAESGCM(symmetricKey, ciphertext);
const token = toHex(plaintextBuffer);
return token;
} catch (e) {
window.log.error('Failed to decrypt token open group v2');
return null;
}
}
/**
*
*/
@ -194,7 +167,7 @@ export async function openGroupV2GetRoomInfo({
isAuthRequired: false,
endpoint: `rooms/${roomId}`,
};
const result = (await sendOpenGroupV2Request(request)) as any;
const result = (await sendApiV2Request(request)) as any;
if (result?.result?.room) {
const { id, name, image_id: imageId } = result?.result?.room;
@ -214,105 +187,6 @@ export async function openGroupV2GetRoomInfo({
return null;
}
async function claimAuthToken(
authToken: string,
serverUrl: string,
roomId: string
): Promise<string | null> {
// Set explicitly here because is isn't in the database yet at this point
const headers = { Authorization: authToken };
const request: OpenGroupV2Request = {
method: 'POST',
headers,
room: roomId,
server: serverUrl,
queryParams: { public_key: getOurPubKeyStrFromCache() },
isAuthRequired: false,
endpoint: 'claim_auth_token',
};
const result = await sendOpenGroupV2Request(request);
const statusCode = parseStatusCodeFromOnionRequest(result);
if (statusCode !== 200) {
window.log.warn(`Could not claim token, status code: ${statusCode}`);
return null;
}
return authToken;
}
export async function getAuthToken({
serverUrl,
roomId,
}: OpenGroupRequestCommonType): Promise<string | null> {
// first try to fetch from db a saved token.
const roomDetails = await getV2OpenGroupRoomByRoomId({ serverUrl, roomId });
if (!roomDetails) {
window.log.warn('getAuthToken Room does not exist.');
return null;
}
if (roomDetails?.token) {
return roomDetails.token;
}
await allowOnlyOneAtATime(`getAuthTokenV2${serverUrl}:${roomId}`, async () => {
try {
window.log.info('TRIGGERING NEW AUTH TOKEN WITH', { serverUrl, roomId });
const token = await requestNewAuthToken({ serverUrl, roomId });
if (!token) {
window.log.warn('invalid new auth token', token);
return;
}
const claimedToken = await claimAuthToken(token, serverUrl, roomId);
if (!claimedToken) {
window.log.warn('invalid claimed token', claimedToken);
}
// still save it to the db. just to mark it as to be refreshed later
roomDetails.token = claimedToken || '';
await saveV2OpenGroupRoom(roomDetails);
} catch (e) {
window.log.error('Failed to getAuthToken', e);
throw e;
}
});
const refreshedRoomDetails = await getV2OpenGroupRoomByRoomId({
serverUrl,
roomId,
});
if (!refreshedRoomDetails) {
window.log.warn('getAuthToken Room does not exist.');
return null;
}
if (refreshedRoomDetails?.token) {
return refreshedRoomDetails?.token;
}
return null;
}
export const deleteAuthToken = async ({
serverUrl,
roomId,
}: OpenGroupRequestCommonType): Promise<boolean> => {
const request: OpenGroupV2Request = {
method: 'DELETE',
room: roomId,
server: serverUrl,
isAuthRequired: false,
endpoint: 'auth_token',
};
try {
const result = await sendOpenGroupV2Request(request);
const statusCode = parseStatusCodeFromOnionRequest(result);
if (statusCode !== 200) {
window.log.warn(`Could not deleteAuthToken, status code: ${statusCode}`);
return false;
}
return true;
} catch (e) {
window.log.error('deleteAuthToken failed:', e);
return false;
}
};
/**
* Send the specified message to the specified room.
* If an error happens, this function throws it
@ -333,7 +207,7 @@ export const postMessage = async (
isAuthRequired: true,
endpoint: 'messages',
};
const result = await sendOpenGroupV2Request(request);
const result = await sendApiV2Request(request);
const statusCode = parseStatusCodeFromOnionRequest(result);
if (statusCode !== 200) {
@ -360,7 +234,7 @@ export const banUser = async (
queryParams,
endpoint: 'block_list',
};
const banResult = await sendOpenGroupV2Request(request);
const banResult = await sendApiV2Request(request);
const isOk = parseStatusCodeFromOnionRequest(banResult) === 200;
return isOk;
};
@ -376,7 +250,7 @@ export const unbanUser = async (
isAuthRequired: true,
endpoint: `block_list/${userToBan.key}`,
};
const unbanResult = await sendOpenGroupV2Request(request);
const unbanResult = await sendApiV2Request(request);
const isOk = parseStatusCodeFromOnionRequest(unbanResult) === 200;
return isOk;
};
@ -393,7 +267,7 @@ export const deleteMessageByServerIds = async (
endpoint: 'delete_messages',
queryParams: { ids: idsToRemove },
};
const messageDeletedResult = await sendOpenGroupV2Request(request);
const messageDeletedResult = await sendApiV2Request(request);
const isOk = parseStatusCodeFromOnionRequest(messageDeletedResult) === 200;
return isOk;
};
@ -408,7 +282,7 @@ export const getAllRoomInfos = async (roomInfos: OpenGroupV2Room) => {
endpoint: 'rooms',
serverPublicKey: roomInfos.serverPublicKey,
};
const result = await sendOpenGroupV2Request(request);
const result = await sendApiV2Request(request);
const statusCode = parseStatusCodeFromOnionRequest(result);
if (statusCode !== 200) {
@ -429,7 +303,7 @@ export const getMemberCount = async (
isAuthRequired: true,
endpoint: 'member_count',
};
const result = await sendOpenGroupV2Request(request);
const result = await sendApiV2Request(request);
if (parseStatusCodeFromOnionRequest(result) !== 200) {
window.log.warn('getMemberCount failed invalid status code');
return;
@ -463,7 +337,7 @@ export const downloadFileOpenGroupV2 = async (
endpoint: `files/${fileId}`,
};
const result = await sendOpenGroupV2Request(request);
const result = await sendApiV2Request(request);
const statusCode = parseStatusCodeFromOnionRequest(result);
if (statusCode !== 200) {
return null;
@ -490,7 +364,7 @@ export const downloadFileOpenGroupV2ByUrl = async (
endpoint: pathName,
};
const result = await sendOpenGroupV2Request(request);
const result = await sendApiV2Request(request);
const statusCode = parseStatusCodeFromOnionRequest(result);
if (statusCode !== 200) {
return null;
@ -522,7 +396,7 @@ export const downloadPreviewOpenGroupV2 = async (
serverPublicKey: roomInfos.serverPublicKey,
};
const result = await sendOpenGroupV2Request(request);
const result = await sendApiV2Request(request);
const statusCode = parseStatusCodeFromOnionRequest(result);
if (statusCode !== 200) {
return null;
@ -561,7 +435,7 @@ export const uploadFileOpenGroupV2 = async (
queryParams,
};
const result = await sendOpenGroupV2Request(request);
const result = await sendApiV2Request(request);
const statusCode = parseStatusCodeFromOnionRequest(result);
if (statusCode !== 200) {
return null;
@ -601,7 +475,7 @@ export const uploadImageForRoomOpenGroupV2 = async (
queryParams,
};
const result = await sendOpenGroupV2Request(request);
const result = await sendApiV2Request(request);
const statusCode = parseStatusCodeFromOnionRequest(result);
if (statusCode !== 200) {
return null;
@ -626,7 +500,7 @@ export const addModerator = async (
queryParams: { public_key: userToAddAsMods.key, room_id: roomInfos.roomId },
endpoint: 'moderators',
};
const addModResult = await sendOpenGroupV2Request(request);
const addModResult = await sendApiV2Request(request);
const isOk = parseStatusCodeFromOnionRequest(addModResult) === 200;
return isOk;
};
@ -642,7 +516,7 @@ export const removeModerator = async (
isAuthRequired: true,
endpoint: `moderators/${userToAddAsMods.key}`,
};
const removeModResult = await sendOpenGroupV2Request(request);
const removeModResult = await sendApiV2Request(request);
const isOk = parseStatusCodeFromOnionRequest(removeModResult) === 200;
return isOk;
};

View file

@ -8,7 +8,8 @@ import { parseStatusCodeFromOnionRequest } from './OpenGroupAPIV2Parser';
import _ from 'lodash';
import { sendViaOnion } from '../../session/onions/onionSend';
import { OpenGroupMessageV2 } from './OpenGroupMessageV2';
import { downloadPreviewOpenGroupV2, getAuthToken, getMemberCount } from './OpenGroupAPIV2';
import { downloadPreviewOpenGroupV2, getMemberCount } from './OpenGroupAPIV2';
import { getAuthToken } from './ApiAuth';
const COMPACT_POLL_ENDPOINT = 'compact_poll';
@ -247,7 +248,7 @@ async function sendOpenGroupV2RequestCompactPoll(
const statusCode = parseStatusCodeFromOnionRequest(res);
if (!statusCode) {
window.log.warn('sendOpenGroupV2Request Got unknown status code; res:', res);
window.log.warn('sendOpenGroupV2RequestCompactPoll Got unknown status code; res:', res);
return null;
}

View file

@ -10,10 +10,11 @@ import { ConversationController } from '../../session/conversations';
import { allowOnlyOneAtATime } from '../../session/utils/Promise';
import { getOpenGroupV2ConversationId } from '../utils/OpenGroupUtils';
import { OpenGroupRequestCommonType } from './ApiUtil';
import { deleteAuthToken, openGroupV2GetRoomInfo } from './OpenGroupAPIV2';
import { openGroupV2GetRoomInfo } from './OpenGroupAPIV2';
import { OpenGroupServerPoller } from './OpenGroupServerPoller';
import _ from 'lodash';
import { deleteAuthToken } from './ApiAuth';
export class OpenGroupManagerV2 {
public static readonly useV2OpenGroups = false;

View file

@ -65,26 +65,6 @@ export function getCompleteUrlFromRoom(roomInfos: OpenGroupV2Room) {
return `${roomInfos.serverUrl}/${roomInfos.roomId}?${publicKeyParam}${roomInfos.serverPublicKey}`;
}
/**
* This function returns a base url to this room
* This is basically used for building url after posting an attachment
* hasRoomInEndpoint = true means the roomId is already in the endpoint.
* so we don't add the room after the serverUrl.
*
*/
export function getCompleteEndpointUrl(
roomInfos: OpenGroupRequestCommonType,
endpoint: string,
hasRoomInEndpoint: boolean
) {
// serverUrl has the port and protocol already
if (!hasRoomInEndpoint) {
return `${roomInfos.serverUrl}/${roomInfos.roomId}/${endpoint}`;
}
// not room based, the endpoint already has the room in it
return `${roomInfos.serverUrl}/${endpoint}`;
}
/**
* Tries to establish a connection with the specified open group url.
*

View file

@ -10,6 +10,7 @@ import {
downloadFileOpenGroupV2ByUrl,
} from '../opengroup/opengroupV2/OpenGroupAPIV2';
import { OpenGroupRequestCommonType } from '../opengroup/opengroupV2/ApiUtil';
import { FSv2 } from '../fileserver';
export async function downloadAttachment(attachment: any) {
const serverUrl = new URL(attachment.url).origin;
@ -19,11 +20,20 @@ export async function downloadAttachment(attachment: any) {
['https://file-static.lokinet.org', 'https://file.getsession.org'],
serverUrl
);
// is it an attachment hosted on the file server v2 ?
const defaultFsV2 = _.startsWith(serverUrl, FSv2.fileServerV2URL);
let res: ArrayBuffer | null = null;
// TODO: we need attachments to remember which API should be used to retrieve them
if (!defaultFileserver) {
if (defaultFsV2) {
if (!attachment.id) {
window.log.warn('Cannot download fsv2 file with empty id');
return;
}
window.log.info('Download v2 file server attachment');
res = await FSv2.downloadFileFromFSv2(attachment.id);
} else if (!defaultFileserver) {
// TODO: we need attachments to remember which API should be used to retrieve them
const serverAPI = await window.lokiPublicChatAPI.findOrCreateServer(serverUrl);
if (serverAPI) {
@ -41,18 +51,6 @@ export async function downloadAttachment(attachment: any) {
throw new Error(`Failed to download attachment. Length is 0 for ${attachment.url}`);
}
// FIXME "178" test to remove once this is fixed server side.
if (!window.lokiFeatureFlags.useFileOnionRequestsV2) {
if (res.byteLength === 178) {
window.log.error(
'Data of 178 length corresponds of a 404 returned as 200 by file.getsession.org.'
);
throw new Error(`downloadAttachment: invalid response for ${attachment.url}`);
}
} else {
// if useFileOnionRequestsV2 is true, we expect an ArrayBuffer not empty
}
// The attachment id is actually just the absolute url of the attachment
let data = res;
if (!attachment.isRaw) {

View file

@ -15,9 +15,9 @@ import { getSnodesFor } from '../snode_api/snodePool';
import { PubKey } from '../types';
import { actions as conversationActions } from '../../state/ducks/conversations';
import { getV2OpenGroupRoom, removeV2OpenGroupRoom } from '../../data/opengroups';
import { deleteAuthToken } from '../../opengroup/opengroupV2/OpenGroupAPIV2';
import _ from 'lodash';
import { OpenGroupManagerV2 } from '../../opengroup/opengroupV2/OpenGroupManagerV2';
import { deleteAuthToken } from '../../opengroup/opengroupV2/ApiAuth';
export class ConversationController {
private static instance: ConversationController | null;

View file

@ -8,6 +8,7 @@ import {
QuotedAttachment,
} from '../messages/outgoing/visibleMessage/VisibleMessage';
import { OpenGroup } from '../../opengroup/opengroupV1/OpenGroup';
import { FSv2 } from '../../fileserver';
interface UploadParams {
attachment: Attachment;
@ -55,6 +56,7 @@ export class AttachmentUtils {
}
let server = window.tokenlessFileServerAdnAPI;
// this can only be an opengroupv1
if (openGroup) {
const openGroupServer = await window.lokiPublicChatAPI.findOrCreateServer(openGroup.server);
if (!openGroupServer) {
@ -92,12 +94,23 @@ export class AttachmentUtils {
attachmentData = data.ciphertext;
}
const result = isAvatar
? await server.putAvatar(attachmentData)
: await server.putAttachment(attachmentData);
// use file server v2
pointer.id = result.id;
pointer.url = result.url;
if (FSv2.useFileServerAPIV2Sending) {
const uploadToV2Result = await FSv2.uploadFileToFsV2(attachmentData);
if (uploadToV2Result) {
pointer.id = uploadToV2Result.fileId;
pointer.url = uploadToV2Result.fileUrl;
} else {
console.warn('upload to file server v2 failed');
}
} else {
const result = isAvatar
? await server.putAvatar(attachmentData)
: await server.putAttachment(attachmentData);
pointer.id = result.id;
pointer.url = result.url;
}
return pointer;
}