import { AbortSignal } from 'abort-controller'; import { APPLICATION_JSON, APPLICATION_OCTET_STREAM } from '../../../../types/MIME'; import { OnionSending } from '../../../onions/onionSend'; import { UserUtils } from '../../../utils'; import { OpenGroupCapabilityRequest } from '../opengroupV2/ApiUtil'; import { OpenGroupMessageV2 } from '../opengroupV2/OpenGroupMessageV2'; import { OpenGroupPollingUtils, OpenGroupRequestHeaders, } from '../opengroupV2/OpenGroupPollingUtils'; import { batchGlobalIsSuccess, parseBatchGlobalStatusCode } from './sogsV3BatchPoll'; export function addJsonContentTypeToHeaders( headers: OpenGroupRequestHeaders ): OpenGroupRequestHeaders { return { ...headers, 'Content-Type': APPLICATION_JSON }; } export function addBinaryContentTypeToHeaders( headers: OpenGroupRequestHeaders ): OpenGroupRequestHeaders { return { ...headers, 'Content-Type': APPLICATION_OCTET_STREAM }; } export type OpenGroupSendMessageRequest = OpenGroupCapabilityRequest & { blinded: boolean; }; export const sendSogsMessageOnionV4 = async ( serverUrl: string, room: string, abortSignal: AbortSignal, message: OpenGroupMessageV2, blinded: boolean ): Promise => { const allValidRoomInfos = OpenGroupPollingUtils.getAllValidRoomInfos(serverUrl, new Set([room])); if (!allValidRoomInfos?.length) { window?.log?.info('getSendMessageRequest: no valid roominfos got.'); throw new Error(`Could not find sogs pubkey of url:${serverUrl}`); } const endpoint = `/room/${room}/message`; const method = 'POST'; const serverPubkey = allValidRoomInfos[0].serverPublicKey; const ourKeyPair = await UserUtils.getIdentityKeyPair(); // if we are sending a blinded message, we have to sign it with the derived keypair // otherwise, we just sign it with our real keypair const signedMessage = blinded ? await message.signWithBlinding(serverPubkey) : await message.sign(ourKeyPair); const json = signedMessage.toJson(); const stringifiedBody = JSON.stringify(json); const result = await OnionSending.sendJsonViaOnionV4ToSogs({ serverUrl, endpoint, serverPubkey, method, abortSignal, blinded, stringifiedBody, headers: null, throwErrors: true, }); if (!batchGlobalIsSuccess(result)) { window?.log?.warn('sendSogsMessageWithOnionV4 Got unknown status code; res:', result); throw new Error( `sendSogsMessageOnionV4: invalid status code: ${parseBatchGlobalStatusCode(result)}` ); } if (!result) { throw new Error('Could not postMessage, res is invalid'); } const rawMessage = result.body as Record; if (!rawMessage) { throw new Error('postMessage parsing failed'); } const toParse = { data: rawMessage.data, server_id: rawMessage.id, public_key: rawMessage.session_id, timestamp: Math.floor(rawMessage.posted * 1000), signature: rawMessage.signature, }; // this will throw if the json is not valid const parsed = OpenGroupMessageV2.fromJson(toParse); return parsed; }; export const sendMessageOnionV4BlindedRequest = async ( serverUrl: string, room: string, abortSignal: AbortSignal, message: OpenGroupMessageV2, recipientBlindedId: string ): Promise<{ serverId: number; serverTimestamp: number }> => { const allValidRoomInfos = OpenGroupPollingUtils.getAllValidRoomInfos(serverUrl, new Set([room])); if (!allValidRoomInfos?.length) { window?.log?.info('getSendMessageRequest: no valid roominfos got.'); throw new Error(`Could not find sogs pubkey of url:${serverUrl}`); } const endpoint = `/inbox/${recipientBlindedId}`; const method = 'POST'; const serverPubkey = allValidRoomInfos[0].serverPublicKey; // if we are sending a blinded message, we have to sign it with the derived keypair // otherwise, we just sign it with our real keypair const signedMessage = await message.signWithBlinding(serverPubkey); const json = signedMessage.toBlindedMessageRequestJson(); const stringifiedBody = JSON.stringify(json); const result = await OnionSending.sendJsonViaOnionV4ToSogs({ serverUrl, endpoint, serverPubkey, method, abortSignal, blinded: true, stringifiedBody, headers: null, throwErrors: true, }); if (!batchGlobalIsSuccess(result)) { window?.log?.warn('sendMessageOnionV4BlindedRequest Got unknown status code; res:', result); throw new Error( `sendMessageOnionV4BlindedRequest: invalid status code: ${parseBatchGlobalStatusCode(result)}` ); } if (!result) { throw new Error('Could not postMessage, res is invalid'); } const rawMessage = result.body as Record; if (!rawMessage) { throw new Error('postMessage parsing failed'); } const serverId = rawMessage.id as number | undefined; const serverTimestamp = rawMessage.posted_at as number | undefined; if (!serverTimestamp || serverId === undefined) { window.log.warn('Could not blinded message request, server returned invalid data:', rawMessage); throw new Error('Could not blinded message request, server returned invalid data'); } return { serverId, serverTimestamp: Math.floor(serverTimestamp * 1000) }; //timestamp are now returned with a seconds.ms syntax };