VulpesOS/assets/webapps/fl.gaiamobile.org/js/download.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

55 lines
No EOL
13 KiB
JavaScript

;'use strict';window.addEventListener('load',function(){navigator.mozL10n.once(function(){navigator.mozSetMessageHandler('activity',view);});});function view(activity){debug('view() invoked for activity',activity.source.name,'with data',JSON.stringify(activity.source.data));var descriptor={};var mimeType;var isImage=false;var isAudio=false;var installType;var hasDescriptor;var activityType=normalizeMimeType(activity.source.data.type);if(activityType==='application/vnd.oma.drm.message'){hasDescriptor=false;var url=(activity.source.data.url||'');descriptor.name=getFileNameFromURL(url);descriptor.size=0;descriptor.types=['application/vnd.oma.drm.message'];descriptor.objectURI=url;displayDownloadProgress();}else{hasDescriptor=true;downloadDescriptor(activity.source.data.url);}
function normalizeMimeType(type){type=type.split(';')[0];type=type.toLowerCase();if(type in MimeTypeAliases){type=MimeTypeAliases[type];}
return type;}
function askSongOrRingtone(aCallback){showDialog({message:_('ringtone-or-song-question'),okText:_('ringtone-response'),cancelText:_('song-response'),okCallback:aCallback.bind(undefined,RINGTONE),cancelCallback:aCallback.bind(undefined,SONG)});}
function getFileNameFromURL(aUri){var name='';if(aUri){name=aUri.split('/').pop();if(name.endsWith('.dm')){name=name.slice(0,-3);}
if(name.length>18){name=name.substring(0,15)+'\u2026';}}
return name;}
function getFileNameFromHeader(aHeader){var regexp=/filename="([^"]*)"/i;var match=regexp.exec(aHeader);var name=match&&match[1];if(name){name=getFileNameFromURL(name);}
return name;}
function reportError(type,errorID,downloadStatus,details){console.error(type,errorID,downloadStatus,details);if(downloadStatus){sendStatus(downloadStatus);}
details='['+(details?errorID+': '+details:errorID)+']';showDialog({title:_(type)||type,message:_(errorID),details:details,okCallback:function(){activity.postError(errorID);}});}
function reportSuccess(successID){sendStatus(OMADownloadStatus.success);showDialog({message:_(successID)||successID,okCallback:function(){activity.postResult({success:true});}});}
function sendStatus(status,callback){if(descriptor.installNotifyURI){var xhr=new XMLHttpRequest(systemXHR);xhr.open('POST',descriptor.installNotifyURI);xhr.timeout=1000;xhr.send(status);if(callback){xhr.onloadend=function(){callback();};}}else if(callback){callback();}}
function downloadDescriptor(descriptorURL){debug('Step 1: downloading descriptor from',descriptorURL);var xhr=new XMLHttpRequest(systemXHR);xhr.open('GET',descriptorURL);xhr.responseType='document';xhr.send();xhr.onerror=function(e){reportError(DOWNLOAD_ERROR,ERR_DESCRIPTOR_DOWNLOAD_FAILED,null,xhr.status+' '+xhr.statusText);};xhr.onload=function(){setTimeout(function(){parse(xhr.response);});};}
function parse(document){var types=document.getElementsByTagName('type');if(types){descriptor.types=[];for(var i=0;i<types.length;i++){descriptor.types.push(normalizeMimeType(types[i].textContent.trim()));}}
function get(tag){var elts=document.getElementsByTagName(tag);if(elts&&elts.length){descriptor[tag]=elts[0].textContent.trim();}}
get('size');get('objectURI');get('installNotifyURI');get('DDVersion');get('name');get('description');get('vendor');debug('Step 2: parse descriptor:',JSON.stringify(descriptor));if(!descriptor.types||descriptor.types.length===0||!descriptor.size||!descriptor.objectURI){reportError(DOWNLOAD_ERROR,ERR_BAD_DESCRIPTOR,OMADownloadStatus.INVALID_DESCRIPTOR);return;}
if(descriptor.size>MAX_DOWNLOAD_SIZE){MediaUtils.getLocalizedSize(descriptor.size).then((msg)=>{reportError(DOWNLOAD_ERROR,ERR_TOO_BIG,OMADownloadStatus.INSUFFICIENT_MEMORY,msg);});return;}
if(!descriptor.name){descriptor.name=getFileNameFromURL(descriptor.objectURI);}
if(descriptor.DDVersion&&parseInt(descriptor.DDVersion)>1){reportError(DOWNLOAD_ERROR,ERR_BAD_DESCRIPTOR,OMADownloadStatus.INVALID_DDVERSION);return;}
checkTypes();}
function checkTypes(){debug('Step 3: check types');var types=descriptor.types;for(var i=0;i<types.length;i++){var type=types[i];if(type==='application/vnd.oma.drm.message'){continue;}
mimeType=type;if(type in SupportedImageTypes){isImage=true;}else if(type in SupportedAudioTypes){isAudio=true;}else{mimeType=undefined;reportError(DOWNLOAD_ERROR,ERR_UNSUPPORTED_TYPE,OMADownloadStatus.NON_ACCEPTABLE_CONTENT,type);return;}}
if(types.length<1||types.length>2||(isAudio&&isImage)||(!isAudio&&!isImage)){reportError(DOWNLOAD_ERROR,ERR_BAD_TYPE,OMADownloadStatus.NON_ACCEPTABLE_CONTENT);return;}
if(isImage){installType=WALLPAPER;confirmDownload();}else{askSongOrRingtone(function(response){installType=response;confirmDownload();});}}
function confirmDownload(){debug('Step 4: confirm download');var titleElement=$('download-confirmation-title');titleElement.textContent=_('download-confirmation-title-'+installType);$('download-confirmation-name').textContent=descriptor.name;$('download-confirmation-type').textContent=mimeType;MediaUtils.getLocalizedSizeTokens(descriptor.size).then((args)=>{navigator.mozL10n.setAttributes($('download-confirmation-size'),'fileSize',args);});$('download-confirmation-description').textContent=descriptor.description;var lca;if(descriptor.vendor){lca=_('locked-content-advisory',{vendor:descriptor.vendor});}else{lca=_('locked-content-advisory-generic');}
$('locked-content-advisory-text').textContent=lca;$('download-confirm').onclick=function(e){e.target.disabled=true;displayDownloadProgress();};$('download-cancel').onclick=function(e){debug('download cancelled');$('download-confirmation').hidden=true;sendStatus(OMADownloadStatus.USER_CANCELLED,function(){activity.postError('cancelled');});};$('download-confirmation').hidden=false;}
function displayDownloadProgress(){debug('Step 5: download content and display progress');var bar;var contentLength;if(!hasDescriptor){$('direct-download').hidden=false;bar=$('direct-download-progress');bar.max=100;}else{var titleElement=$('download-confirmation-title');titleElement.textContent=_('downloading-'+installType);$('download-confirmation').hidden=false;bar=$('download-progress');bar.max=descriptor.size;}
bar.style.visibility='visible';bar.value=0;debug('starting download for',descriptor.objectURI);var download=new XMLHttpRequest(systemXHR);download.open('GET',descriptor.objectURI);download.responseType='arraybuffer';download.send();download.onprogress=function(e){if(contentLength===undefined){contentLength=download.getResponseHeader('Content-Length');bar.max=contentLength&&parseInt(contentLength)||bar.max;}
if(descriptor.size||contentLength){bar.value=e.loaded;}else{bar.value=(bar.value+10)%bar.max;}};download.onerror=function(){$('download-confirmation').hidden=true;reportError(DOWNLOAD_ERROR,ERR_CONTENT_DOWNLOAD_FAILED,OMADownloadStatus.LOADER_ERROR,download.status+' '+download.statusText);};download.onload=function(){$('download-cancel').disabled=true;$('download-confirmation').hidden=true;var cdHeader=download.getResponseHeader('Content-Disposition');if(cdHeader&&!hasDescriptor){descriptor.name=getFileNameFromHeader(cdHeader)||descriptor.name;}
var type=normalizeMimeType(download.getResponseHeader('Content-Type'));extractContent(type,download.response);};$('download-cancel').addEventListener('click',function(){download.abort();});}
function extractContent(type,buffer){debug('Step 6a: extractContent',type,buffer.byteLength);if(type!=='application/vnd.oma.drm.message'){previewContent(type,buffer);return;}
var dataString=new BinaryStringView(buffer);dataString=dataString.trim();function malformed(why){if(why){debug('malformed drm message:',why);}
reportError(DOWNLOAD_ERROR,ERR_BAD_DRM_MESSAGE,OMADownloadStatus.ATTRIBUTE_MISMATCH);}
if(dataString.slice(-2).toString()!=='--'){return malformed('missing trailing --');}
var end=dataString.lastIndexOf('\r\n');if(end===-1){return malformed('missing final \r\n');}
var boundaryString=dataString.slice(end+2,-2).toString()+'\r\n';dataString=dataString.slice(0,end);debug('Boundary string is',boundaryString);var headerStart=dataString.indexOf(boundaryString)+boundaryString.length;if(headerStart===-1){return malformed('missing boundary string at start');}
if(dataString.indexOf(boundaryString,headerStart)!==-1){return malformed('more than one part');}
var contentStart=dataString.indexOf('\r\n\r\n',headerStart);if(contentStart===-1){return malformed('missing header separator');}
var headers=dataString.slice(headerStart,contentStart).toString().split('\r\n');var content=dataString.slice(contentStart+4);debug('got first part headers:',JSON.stringify(headers));var contentType;for(var i=0;i<headers.length;i++){var header=headers[i].toLowerCase();if(header.startsWith('content-type:')){contentType=normalizeMimeType(header.substring(13).trim());}}
if(!contentType){return malformed('no content-type header found in first part');}
debug('Found content in drm message file',contentType,content.length);previewContent(contentType,content.toArrayBuffer());}
function previewContent(type,bytes){if(!hasDescriptor){mimeType=type;if(type in SupportedImageTypes){isImage=true;}else if(type in SupportedAudioTypes){isAudio=true;}else{mimeType=undefined;reportError(DOWNLOAD_ERROR,ERR_UNSUPPORTED_TYPE,OMADownloadStatus.NON_ACCEPTABLE_CONTENT,type);return;}}else{if(type!==mimeType){reportError(DOWNLOAD_ERROR,ERR_BAD_DRM_MESSAGE,OMADownloadStatus.ATTRIBUTE_MISMATCH);return;}}
if(isImage){installType=WALLPAPER;previewImage(type,bytes);}else{if(!hasDescriptor){askSongOrRingtone(function(response){installType=response;previewAudio(type,bytes);});}else{previewAudio(type,bytes);}}}
function previewImage(type,bytes){$('download-confirmation').hidden=true;$('preview-image-screen').hidden=false;var blob=new Blob([bytes],{type:type});var url=URL.createObjectURL(blob);var image=new Image();image.src=url;image.onerror=function(){URL.revokeObjectURL(url);reportError(INSTALL_ERROR,ERR_BAD_IMAGE,OMADownloadStatus.NON_ACCEPTABLE_CONTENT);return;};image.onload=function(){var preview=$('preview-image');preview.style.backgroundImage='url('+url+')';$('preview-image-install').disabled=false;};$('preview-image-install').onclick=function(){URL.revokeObjectURL(url);installContent(bytes);};$('preview-image-cancel').onclick=function(){URL.revokeObjectURL(url);sendStatus(OMADownloadStatus.USER_CANCELLED,function(){activity.postError('cancelled');});};}
function previewAudio(type,bytes){$('download-confirmation').hidden=true;$('preview-audio-screen').hidden=false;var blob=new Blob([bytes],{type:type});var url=URL.createObjectURL(blob);var player=$('preview-audio');player.src=url;player.onerror=function(){cleanup();reportError(INSTALL_ERROR,ERR_BAD_AUDIO,OMADownloadStatus.NON_ACCEPTABLE_CONTENT);return;};player.oncanplay=function(){$('preview-audio-install').disabled=false;};player.ontimeupdate=player.ondurationchange=function(){if(player.currentTime>Math.max(5,player.duration/2)){player.pause();player.currentTime=0;}};$('preview-audio-install').onclick=function(){cleanup();installContent(bytes);};$('preview-audio-cancel').onclick=function(){cleanup();sendStatus(OMADownloadStatus.USER_CANCELLED,function(){activity.postError('cancelled');});};function cleanup(){player.pause();player.removeAttribute('src');player.load();URL.revokeObjectURL(url);}}
function installContent(bytes){switch(installType){case SONG:installSong(bytes);return;case RINGTONE:case WALLPAPER:install(installType,bytes,descriptor);return;}}
function installSong(buffer){debug('installSong');ForwardLock.getOrCreateKey(function(secret){debug('got secret key');var blob=ForwardLock.lockBuffer(secret,buffer,mimeType,{vendor:descriptor.vendor,name:descriptor.name,description:descriptor.description});debug('encrypted song');getStorageIfAvailable('music',blob.size,storageSuccess,storageError);function storageError(err){debug('storageError:',err);if(err==='unavailable'){reportError(INSTALL_ERROR,ERR_NO_SDCARD,OMADownloadStatus.INSUFFICIENT_MEMORY);}else if(err==='shared'){reportError(INSTALL_ERROR,ERR_SDCARD_IN_USE,OMADownloadStatus.INSUFFICIENT_MEMORY);}else{reportError(INSTALL_ERROR,ERR_NO_SPACE,OMADownloadStatus.INSUFFICIENT_MEMORY,err);}}
function storageSuccess(storage){debug('got music storage');var filename='locked/';if(descriptor.vendor){filename+=fixPath(descriptor.vendor)+'/';}
filename+=fixPath(descriptor.name)+'.lcka';debug('trying filename',filename);function fixPath(s){return s.replace(/[\/\\\.\,\?\*\:]/g,'');}
getUnusedFilename(storage,filename,function(filename){debug('using filename',filename);var addreq=storage.addNamed(blob,filename);addreq.onerror=function(){reportError(INSTALL_ERROR,ERR_DS_SAVE_FAILURE,OMADownloadStatus.LOADER_ERROR,addreq.error.name);};addreq.onsuccess=function(){reportSuccess(SUCCESS_SONG);};});}});}
function install(installType,buffer,descriptor){ForwardLock.getOrCreateKey();var objectStoreName=installType===RINGTONE?'ringtones':'wallpapers';var blob=new Blob([buffer],{type:mimeType});objectStore.readwrite(objectStoreName,function(store){var req=store.add({blob:blob,descriptor:descriptor});req.onerror=function(){reportError(INSTALL_ERROR,ERR_DB_STORE_FAILURE,OMADownloadStatus.LOADER_ERROR,req.error.name);};req.onsuccess=function(){sendStatus(OMADownloadStatus.SUCCESS);var lock=navigator.mozSettings.createLock();var settings={};if(installType===RINGTONE){settings[RINGTONE_KEY]=blob;settings[RINGTONE_NAME_KEY]=descriptor.name;settings[RINGTONE_ID_KEY]=req.result;}
else{settings[WALLPAPER_KEY]=blob;}
lock.set(settings).onsuccess=function(){var msgid;if(installType===RINGTONE){msgid=SUCCESS_RINGTONE;}else{msgid=SUCCESS_WALLPAPER;}
showDialog({message:_(msgid),okCallback:function(){activity.postResult({success:true});}});};};});}}