diff --git a/start.sh b/start.sh index 9682b1e..330f1cd 100644 --- a/start.sh +++ b/start.sh @@ -679,8 +679,10 @@ chmod 644 /app/data/web/runtime-config.js # Create a custom URL patch file to fix the URL constructor error echo "==> Creating URL and SRP patch file" -cat << EOF > /app/data/web/ente-patches.js +cat > /app/data/web/photos/static/ente-patches.js << 'ENDPATCHES' (function() { + console.log('Applying Ente URL and SRP patches...'); + // Save original URL constructor const originalURL = window.URL; @@ -710,102 +712,364 @@ cat << EOF > /app/data/web/ente-patches.js } }; - // More robust Buffer implementation for SRP - window.Buffer = window.Buffer || { + // Comprehensive Buffer polyfill for SRP + const originalBuffer = window.Buffer; + window.Buffer = { from: function(data, encoding) { - // Handle undefined data - critical fix - if (data === undefined) { - console.warn('Buffer.from called with undefined data, creating empty buffer'); - return { - data: '', + // Debug logging for the SRP calls + console.debug('Buffer.from called with:', + typeof data, + data === undefined ? 'undefined' : + data === null ? 'null' : + Array.isArray(data) ? 'array[' + data.length + ']' : + 'value', + 'encoding:', encoding); + + // Handle undefined/null data - critical fix + if (data === undefined || data === null) { + console.warn('Buffer.from called with ' + (data === undefined ? 'undefined' : 'null') + ' data, creating empty buffer'); + const result = { + data: new Uint8Array(0), length: 0, - toString: function() { return ''; } + toString: function(enc) { return ''; } }; + + // Add additional methods that SRP might use + result.slice = function() { return Buffer.from([]); }; + result.readUInt32BE = function() { return 0; }; + result.writeUInt32BE = function() { return result; }; + + return result; + } + + // Special case for hex strings - very important for SRP + if (typeof data === 'string' && encoding === 'hex') { + // Convert hex string to byte array + const bytes = []; + for (let i = 0; i < data.length; i += 2) { + if (data.length - i >= 2) { + bytes.push(parseInt(data.substr(i, 2), 16)); + } + } + + const result = { + data: new Uint8Array(bytes), + length: bytes.length, + toString: function(enc) { + if (enc === 'hex' || !enc) { + return data; // Return original hex string + } + return bytes.map(b => String.fromCharCode(b)).join(''); + } + }; + + // Add methods needed by SRP + result.slice = function(start, end) { + const slicedData = bytes.slice(start, end); + return Buffer.from(slicedData.map(b => b.toString(16).padStart(2, '0')).join(''), 'hex'); + }; + + result.readUInt32BE = function(offset = 0) { + let value = 0; + for (let i = 0; i < 4; i++) { + value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0); + } + return value; + }; + + result.writeUInt32BE = function(value, offset = 0) { + for (let i = 0; i < 4; i++) { + if (offset + i < bytes.length) { + bytes[offset + 3 - i] = value & 0xFF; + value >>>= 8; + } + } + return result; + }; + + return result; } // Handle string data if (typeof data === 'string') { - return { - data: data, - length: data.length, + const bytes = Array.from(data).map(c => c.charCodeAt(0)); + const result = { + data: new Uint8Array(bytes), + length: bytes.length, toString: function(enc) { - if (enc === 'hex' && encoding === 'hex') { - // Return the original hex string - return data; + if (enc === 'hex') { + return bytes.map(b => b.toString(16).padStart(2, '0')).join(''); } return data; } }; + + // Add SRP methods + result.slice = function(start, end) { + return Buffer.from(data.slice(start, end)); + }; + + result.readUInt32BE = function(offset = 0) { + let value = 0; + for (let i = 0; i < 4; i++) { + value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0); + } + return value; + }; + + result.writeUInt32BE = function(value, offset = 0) { + for (let i = 0; i < 4; i++) { + if (offset + i < bytes.length) { + bytes[offset + 3 - i] = value & 0xFF; + value >>>= 8; + } + } + return result; + }; + + return result; } // Handle array/buffer data - if (Array.isArray(data) || ArrayBuffer.isView(data)) { - return { - data: data, - length: data.length, - toString: function() { - return Array.from(data).map(b => String.fromCharCode(b)).join(''); + if (Array.isArray(data) || ArrayBuffer.isView(data) || (data instanceof ArrayBuffer)) { + const bytes = Array.isArray(data) ? data : new Uint8Array(data.buffer || data); + const result = { + data: new Uint8Array(bytes), + length: bytes.length, + toString: function(enc) { + if (enc === 'hex') { + return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join(''); + } + return Array.from(bytes).map(b => String.fromCharCode(b)).join(''); } }; + + // Add SRP methods + result.slice = function(start, end) { + return Buffer.from(bytes.slice(start, end)); + }; + + result.readUInt32BE = function(offset = 0) { + let value = 0; + for (let i = 0; i < 4; i++) { + value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0); + } + return value; + }; + + result.writeUInt32BE = function(value, offset = 0) { + for (let i = 0; i < 4; i++) { + if (offset + i < bytes.length) { + bytes[offset + 3 - i] = value & 0xFF; + value >>>= 8; + } + } + return result; + }; + + return result; } - // Default fallback - return { - data: data || '', - length: (data && data.length) || 0, - toString: function() { return ''; } + // Handle object data (last resort) + if (typeof data === 'object') { + console.warn('Buffer.from called with object type', data); + const result = { + data: data, + length: data.length || 0, + toString: function() { return JSON.stringify(data); } + }; + + // Add SRP methods + result.slice = function() { return Buffer.from({}); }; + result.readUInt32BE = function() { return 0; }; + result.writeUInt32BE = function() { return result; }; + + return result; + } + + // Default fallback for any other type + console.warn('Buffer.from called with unsupported type:', typeof data); + const result = { + data: new Uint8Array(0), + length: 0, + toString: function() { return ''; }, + slice: function() { return Buffer.from([]); }, + readUInt32BE: function() { return 0; }, + writeUInt32BE: function() { return result; } }; + + return result; }, - isBuffer: function(obj) { return obj && obj.data !== undefined; }, - alloc: function(size) { - const arr = new Array(size).fill(0); - return { - data: arr, + + isBuffer: function(obj) { + return obj && (obj.data !== undefined || (originalBuffer && originalBuffer.isBuffer && originalBuffer.isBuffer(obj))); + }, + + alloc: function(size, fill = 0) { + const bytes = new Array(size).fill(fill); + const result = { + data: new Uint8Array(bytes), length: size, - toString: function() { return ''; } - }; - }, - concat: function(list) { - // Simple implementation that handles our use case - return { - data: list.map(b => b.data).join(''), - length: list.reduce((acc, b) => acc + (b.length || 0), 0), - toString: function() { - return list.map(b => b.toString()).join(''); + toString: function(enc) { + if (enc === 'hex') { + return bytes.map(b => b.toString(16).padStart(2, '0')).join(''); + } + return bytes.map(b => String.fromCharCode(b)).join(''); } }; + + // Add SRP methods + result.slice = function(start, end) { + return Buffer.from(bytes.slice(start, end)); + }; + + result.readUInt32BE = function(offset = 0) { + let value = 0; + for (let i = 0; i < 4; i++) { + value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0); + } + return value; + }; + + result.writeUInt32BE = function(value, offset = 0) { + for (let i = 0; i < 4; i++) { + if (offset + i < bytes.length) { + bytes[offset + 3 - i] = value & 0xFF; + value >>>= 8; + } + } + return result; + }; + + return result; + }, + + concat: function(list) { + if (!Array.isArray(list) || list.length === 0) { + return Buffer.alloc(0); + } + + // Combine all buffers into one + const totalLength = list.reduce((acc, buf) => acc + (buf ? (buf.length || 0) : 0), 0); + const combinedArray = new Uint8Array(totalLength); + + let offset = 0; + for (const buf of list) { + if (buf && buf.data) { + const data = buf.data instanceof Uint8Array ? buf.data : new Uint8Array(buf.data); + combinedArray.set(data, offset); + offset += buf.length; + } + } + + const result = { + data: combinedArray, + length: totalLength, + toString: function(enc) { + if (enc === 'hex') { + return Array.from(combinedArray).map(b => b.toString(16).padStart(2, '0')).join(''); + } + return Array.from(combinedArray).map(b => String.fromCharCode(b)).join(''); + } + }; + + // Add SRP methods + result.slice = function(start, end) { + const slicedData = combinedArray.slice(start, end); + return Buffer.from(slicedData); + }; + + result.readUInt32BE = function(offset = 0) { + let value = 0; + for (let i = 0; i < 4; i++) { + value = (value << 8) + (offset + i < combinedArray.length ? combinedArray[offset + i] : 0); + } + return value; + }; + + result.writeUInt32BE = function(value, offset = 0) { + for (let i = 0; i < 4; i++) { + if (offset + i < combinedArray.length) { + combinedArray[offset + 3 - i] = value & 0xFF; + value >>>= 8; + } + } + return result; + }; + + return result; } }; // Add missing crypto methods that SRP might need - if (window.crypto && !window.crypto.randomBytes) { - window.crypto.randomBytes = function(size) { - const array = new Uint8Array(size); - window.crypto.getRandomValues(array); - return { - data: array, - length: size, - toString: function(encoding) { - if (encoding === 'hex') { - return Array.from(array).map(b => b.toString(16).padStart(2, '0')).join(''); - } - return Array.from(array).map(b => String.fromCharCode(b)).join(''); - } + if (window.crypto) { + if (!window.crypto.randomBytes) { + window.crypto.randomBytes = function(size) { + const array = new Uint8Array(size); + window.crypto.getRandomValues(array); + return Buffer.from(array); }; - }; + } + + // Add cryptographic hash functions if needed + if (!window.crypto.createHash) { + window.crypto.createHash = function(algorithm) { + return { + update: function(data) { + this.data = data; + return this; + }, + digest: async function(encoding) { + // Use the SubtleCrypto API for actual hashing + const dataBuffer = typeof this.data === 'string' ? + new TextEncoder().encode(this.data) : + this.data; + + let hashBuffer; + try { + if (algorithm === 'sha256') { + hashBuffer = await window.crypto.subtle.digest('SHA-256', dataBuffer); + } else if (algorithm === 'sha1') { + hashBuffer = await window.crypto.subtle.digest('SHA-1', dataBuffer); + } else { + console.error('Unsupported hash algorithm:', algorithm); + return Buffer.alloc(32); // Return empty buffer as fallback + } + + const hashArray = Array.from(new Uint8Array(hashBuffer)); + + if (encoding === 'hex') { + return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); + } + + return Buffer.from(hashArray); + } catch (e) { + console.error('Hash calculation failed:', e); + return Buffer.alloc(32); // Return empty buffer as fallback + } + } + }; + }; + } } // Patch the SRP implementation for browser compatibility - if (window.process) { - // Add any missing process methods - window.process.nextTick = window.process.nextTick || function(fn) { - setTimeout(fn, 0); + if (!window.process) { + window.process = { + env: { + NODE_ENV: 'production' + } }; } + // Add any missing process methods + window.process.nextTick = window.process.nextTick || function(fn) { + setTimeout(fn, 0); + }; + console.log('Ente URL and SRP patches applied successfully'); })(); -EOF +ENDPATCHES # Create the static HTML files with scripts pre-injected for app_dir in photos accounts auth cast; do