|
| 1 | +const fs = require('fs'); |
| 2 | +const path = require('path'); |
| 3 | +const execa = require('execa'); |
| 4 | +const tempy = require('tempy'); |
| 5 | + |
| 6 | +function getRtfUnicodeEscapedString(text) { |
| 7 | + let result = ''; |
| 8 | + for (let i = 0; i < text.length; i++) { |
| 9 | + if (text[i] === '\\' || text[i] === '{' || text[i] === '}' || text[i] === '\n') { |
| 10 | + result += `\\${text[i]}`; |
| 11 | + } else if (text[i] === '\r') { |
| 12 | + // ignore |
| 13 | + } else if (text.charCodeAt(i) <= 0x7F) { |
| 14 | + result += text[i]; |
| 15 | + } else { |
| 16 | + result += `\\u${text.codePointAt(i)}?`; |
| 17 | + } |
| 18 | + } |
| 19 | + |
| 20 | + return result; |
| 21 | +} |
| 22 | + |
| 23 | +function wrapInRtf(text) { |
| 24 | + return '\t$"7B5C 7274 6631 5C61 6E73 695C 616E 7369"\n' + |
| 25 | + '\t$"6370 6731 3235 325C 636F 636F 6172 7466"\n' + |
| 26 | + '\t$"3135 3034 5C63 6F63 6F61 7375 6272 7466"\n' + |
| 27 | + '\t$"3833 300A 7B5C 666F 6E74 7462 6C5C 6630"\n' + |
| 28 | + '\t$"5C66 7377 6973 735C 6663 6861 7273 6574"\n' + |
| 29 | + '\t$"3020 4865 6C76 6574 6963 613B 7D0A 7B5C"\n' + |
| 30 | + '\t$"636F 6C6F 7274 626C 3B5C 7265 6432 3535"\n' + |
| 31 | + '\t$"5C67 7265 656E 3235 355C 626C 7565 3235"\n' + |
| 32 | + '\t$"353B 7D0A 7B5C 2A5C 6578 7061 6E64 6564"\n' + |
| 33 | + '\t$"636F 6C6F 7274 626C 3B3B 7D0A 5C70 6172"\n' + |
| 34 | + '\t$"645C 7478 3536 305C 7478 3131 3230 5C74"\n' + |
| 35 | + '\t$"7831 3638 305C 7478 3232 3430 5C74 7832"\n' + |
| 36 | + '\t$"3830 305C 7478 3333 3630 5C74 7833 3932"\n' + |
| 37 | + '\t$"305C 7478 3434 3830 5C74 7835 3034 305C"\n' + |
| 38 | + '\t$"7478 3536 3030 5C74 7836 3136 305C 7478"\n' + |
| 39 | + '\t$"616C 5C70 6172 7469 6768 7465 6E66 6163"\n' + |
| 40 | + '\t$"746F 7230 0A0A 5C66 305C 6673 3234 205C"\n' + |
| 41 | + `${serializeString('63663020' + Buffer.from(getRtfUnicodeEscapedString(text)).toString('hex').toUpperCase() + '7D')}`; |
| 42 | +} |
| 43 | + |
| 44 | +function serializeString(text) { |
| 45 | + return '\t$"' + text.match(/.{1,32}/g).map(x => x.match(/.{1,4}/g).join(' ')).join('"\n\t$"') + '"'; |
| 46 | +} |
| 47 | + |
| 48 | +module.exports = async (dmgPath, dmgFormat) => { |
| 49 | + // Valid SLA filenames |
| 50 | + const rawSlaFile = path.join(process.cwd(), 'sla.r'); |
| 51 | + const rtfSlaFile = path.join(process.cwd(), 'license.rtf'); |
| 52 | + const txtSlaFile = path.join(process.cwd(), 'license.txt'); |
| 53 | + |
| 54 | + const hasRaw = fs.existsSync(rawSlaFile); |
| 55 | + const hasRtf = fs.existsSync(rtfSlaFile); |
| 56 | + const hasTxt = fs.existsSync(txtSlaFile); |
| 57 | + |
| 58 | + if (!hasRaw && !hasRtf && !hasTxt) { |
| 59 | + return; |
| 60 | + } |
| 61 | + |
| 62 | + const tempDmgPath = tempy.file({extension: 'dmg'}); |
| 63 | + |
| 64 | + // UDCO or UDRO format is required to be able to unflatten |
| 65 | + // Convert and unflatten DMG (original format will be restored at the end) |
| 66 | + await execa('/usr/bin/hdiutil', ['convert', '-format', 'UDCO', dmgPath, '-o', tempDmgPath]); |
| 67 | + await execa('/usr/bin/hdiutil', ['unflatten', tempDmgPath]); |
| 68 | + |
| 69 | + if (hasRaw) { |
| 70 | + // If user-defined sla.r file exists, add it to dmg with 'rez' utility |
| 71 | + await execa('/usr/bin/rez', ['-a', rawSlaFile, '-o', tempDmgPath]); |
| 72 | + } else { |
| 73 | + // Generate sla.r file from text/rtf file |
| 74 | + // Use base.r file as a starting point |
| 75 | + let data = fs.readFileSync(path.join(__dirname, 'base.r'), 'utf8'); |
| 76 | + let plainText = ''; |
| 77 | + |
| 78 | + // Generate RTF version and preserve plain text |
| 79 | + data += '\ndata \'RTF \' (5000, "English") {\n'; |
| 80 | + |
| 81 | + if (hasRtf) { |
| 82 | + data += serializeString((fs.readFileSync(rtfSlaFile).toString('hex').toUpperCase())); |
| 83 | + ({stdout: plainText} = await execa('/usr/bin/textutil', ['-convert', 'txt', '-stdout', rtfSlaFile])); |
| 84 | + } else { |
| 85 | + plainText = fs.readFileSync(txtSlaFile, 'utf8'); |
| 86 | + data += wrapInRtf(plainText); |
| 87 | + } |
| 88 | + |
| 89 | + data += '\n};\n'; |
| 90 | + |
| 91 | + // Generate plain text version |
| 92 | + // Used as an alternate for command-line deployments |
| 93 | + data += '\ndata \'TEXT\' (5000, "English") {\n'; |
| 94 | + data += serializeString(Buffer.from(plainText, 'utf8').toString('hex').toUpperCase()); |
| 95 | + data += '\n};\n'; |
| 96 | + |
| 97 | + // Save sla.r file, add it to DMG with `rez` utility |
| 98 | + const tempSlaFile = tempy.file({extension: 'r'}); |
| 99 | + fs.writeFileSync(tempSlaFile, data, 'utf8'); |
| 100 | + await execa('/usr/bin/rez', ['-a', tempSlaFile, '-o', tempDmgPath]); |
| 101 | + } |
| 102 | + |
| 103 | + // Flatten and convert back to original dmgFormat |
| 104 | + await execa('/usr/bin/hdiutil', ['flatten', tempDmgPath]); |
| 105 | + await execa('/usr/bin/hdiutil', ['convert', '-format', dmgFormat, tempDmgPath, '-o', dmgPath, '-ov']); |
| 106 | +}; |
0 commit comments