You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

238 lines
6.9 KiB

import * as path from 'path'
import fetch from 'node-fetch'
import {parseLDAT} from '../utils/ldat'
import * as rsa from '../crypto/rsa'
import * as crypto from 'crypto'
import * as meme from '../utils/meme'
import * as FormData from 'form-data'
import { models } from '../models'
import * as RNCryptor from 'jscryptor'
import {sendMessage} from './send'
// import { Op } from 'sequelize'
const constants = require(path.join(__dirname,'../../config/constants.json'))
const msgtypes = constants.message_types
export async function modifyPayloadAndSaveMediaKey(payload, chat, sender) {
if(payload.type!==msgtypes.attachment) return payload
try{
const ret = await downloadAndUploadAndSaveReturningTermsAndKey(payload,chat,sender)
return fillmsg(payload, ret) // key is re-encrypted later
} catch(e) {
console.log("[modify] error", e)
return payload
}
}
// "purchase" type
export async function purchaseFromOriginalSender(payload, chat, purchaser){
if(payload.type!==msgtypes.purchase) return
const mt = payload.message && payload.message.mediaToken
const amount = payload.message.amount
const muid = mt && mt.split('.').length && mt.split('.')[1]
if(!muid) return
const mediaKey = await models.MediaKey.findOne({where:{originalMuid:muid}})
const terms = parseLDAT(mt)
let price = terms.meta && terms.meta.amt
if(amount<price) return // not enough sats
const owner = await models.Contact.findOne({where: {isOwner:true}})
if(mediaKey) { // ALREADY BEEN PURHCASED! simply send
// send back the new mediaToken and key
const mediaTerms: {[k:string]:any} = {
muid:mediaKey.muid, ttl:31536000, host:'',
meta:{...amount && {amt:amount}},
}
// send full new key and token
const msg = {
mediaTerms,
mediaKey:mediaKey.key,
originalMuid:mediaKey.originalMuid,
mediaType:mediaKey.mediaType
}
sendMessage({
chat: {...chat.dataValues, contactIds:[purchaser.id]}, // the merchant id
sender: owner,
type: constants.message_types.purchase_accept,
message: msg,
success: ()=>{},
failure: ()=>{}
})
// PAY THE OG POSTER HERE!!!
sendMessage({
chat: {...chat.dataValues, contactIds:[mediaKey.sender]},
sender: owner,
type: constants.message_types.purchase,
amount: amount,
message: {
mediaToken: mt,
skipPaymentProcessing: true,
},
success: ()=>{},
failure: ()=>{}
})
} else {
const ogmsg = await models.Message.findOne({where:{chatId:chat.id,mediaToken:mt}})
if(!ogmsg) return
// purchase it from creator (send "purchase")
const msg={mediaToken:mt,purchaser:purchaser.id}
sendMessage({
chat: {...chat.dataValues, contactIds:[ogmsg.sender]},
sender: {
...owner.dataValues,
...purchaser&&purchaser.alias && {alias:purchaser.alias}
},
type: constants.message_types.purchase,
message: msg,
amount: amount,
success: ()=>{},
failure: ()=>{}
})
}
}
export async function sendFinalMemeIfFirstPurchaser(payload, chat, sender){
if(payload.type!==msgtypes.purchase_accept) return
const mt = payload.message && payload.message.mediaToken
const typ = payload.message && payload.message.mediaType
const purchaserID = payload.message && payload.message.purchaser
if(!mt) return
const muid = mt && mt.split('.').length && mt.split('.')[1]
if(!muid) return
const existingMediaKey = await models.MediaKey.findOne({where:{muid}})
if(existingMediaKey) return // no need, its already been sent
// const host = mt.split('.')[0]
const terms = parseLDAT(mt)
const ogPurchaser = await models.Contact.findOne({where:{
id: purchaserID
}})
if(!ogPurchaser) return
const amt = (terms.meta&&terms.meta.amt)||0
// const ogPurchaseMessage = await models.Message.findOne({where:{
// mediaToken: {[Op.like]: `${host}.${muid}%`},
// type: msgtypes.purchase,
// }})
const termsAndKey = await downloadAndUploadAndSaveReturningTermsAndKey(payload,chat,sender,amt)
// send it to the purchaser
const owner = await models.Contact.findOne({where: {isOwner:true}})
sendMessage({
sender: {
...owner.dataValues,
...sender&&sender.alias && {alias:sender.alias}
},
chat:{
...chat.dataValues,
contactIds:[ogPurchaser.id],
},
type:msgtypes.purchase_accept,
message:{
...termsAndKey,
mediaType: typ,
originalMuid:muid,
},
success: ()=>{},
receive: ()=>{}
})
}
function fillmsg(full, props){
return {
...full, message: {
...full.message,
...props,
}
}
}
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
export async function downloadAndUploadAndSaveReturningTermsAndKey(payload, chat, sender, injectedAmount?:number){
const mt = payload.message && payload.message.mediaToken
const key = payload.message && payload.message.mediaKey
const typ = payload.message && payload.message.mediaType
if(!mt || !key) return payload // save anyway??????????
const ogmuid = mt && mt.split('.').length && mt.split('.')[1]
const terms = parseLDAT(mt)
if(!terms.host) return payload
try {
const r = await fetch(`https://${terms.host}/file/${mt}`, {
headers: {'Authorization': `Bearer ${meme.mediaToken}`}
})
const buf = await r.buffer()
const decMediaKey = rsa.decrypt(chat.groupPrivateKey, key)
const imgBuf = RNCryptor.Decrypt(buf.toString('base64'), decMediaKey)
const newKey = crypto.randomBytes(20).toString('hex')
const encImgBase64 = RNCryptor.Encrypt(imgBuf, newKey)
var encImgBuffer = Buffer.from(encImgBase64,'base64');
const form = new FormData()
form.append('file', encImgBuffer, {
contentType: typ||'image/jpg',
filename: 'Image.jpg',
knownLength:encImgBuffer.length,
})
const formHeaders = form.getHeaders()
const resp = await fetch(`https://${terms.host}/file`, {
method: 'POST',
headers: {
...formHeaders, // THIS IS REQUIRED!!!
'Authorization': `Bearer ${meme.mediaToken}`,
},
body:form
})
let json = await resp.json()
if(!json.muid) throw new Error('no muid')
// PUT NEW TERMS, to finish in personalizeMessage
const amt = (terms.meta&&terms.meta.amt)||injectedAmount
const ttl = terms.meta&&terms.meta.ttl
const mediaTerms: {[k:string]:any} = {
muid:json.muid, ttl:ttl||31536000, host:'',
meta:{...amt && {amt}},
}
const encKey = rsa.encrypt(chat.groupKey, newKey.slice())
var date = new Date()
date.setMilliseconds(0)
await sleep(1)
await models.MediaKey.create({
muid:json.muid,
chatId:chat.id,
key:encKey,
messageId: (payload.message&&payload.message.id)||0,
receiver: 0,
sender: sender.id, // the og sender (merchant) who is sending the completed media token
createdAt: date,
originalMuid: ogmuid,
mediaType:typ,
})
return {mediaTerms,mediaKey:encKey}
} catch(e) {
throw e
}
}