|
|
@ -4,7 +4,6 @@ var wappalyzer = (function() {
|
|
|
|
self = {
|
|
|
|
self = {
|
|
|
|
apps: {},
|
|
|
|
apps: {},
|
|
|
|
appsDetected: 0,
|
|
|
|
appsDetected: 0,
|
|
|
|
autoDetect: true,
|
|
|
|
|
|
|
|
browser: false,
|
|
|
|
browser: false,
|
|
|
|
cats: {},
|
|
|
|
cats: {},
|
|
|
|
checkUnique: {},
|
|
|
|
checkUnique: {},
|
|
|
@ -17,6 +16,7 @@ var wappalyzer = (function() {
|
|
|
|
homeUrl: 'http://wappalyzer.com/',
|
|
|
|
homeUrl: 'http://wappalyzer.com/',
|
|
|
|
hoverTimeout: false,
|
|
|
|
hoverTimeout: false,
|
|
|
|
newInstall: false,
|
|
|
|
newInstall: false,
|
|
|
|
|
|
|
|
popupOnHover: true,
|
|
|
|
prevUrl: '',
|
|
|
|
prevUrl: '',
|
|
|
|
prefs: {},
|
|
|
|
prefs: {},
|
|
|
|
regexBlacklist: /(dev\.|\/admin|\.local)/,
|
|
|
|
regexBlacklist: /(dev\.|\/admin|\.local)/,
|
|
|
@ -26,6 +26,7 @@ var wappalyzer = (function() {
|
|
|
|
showApps: 1,
|
|
|
|
showApps: 1,
|
|
|
|
showCats: [],
|
|
|
|
showCats: [],
|
|
|
|
strings: {},
|
|
|
|
strings: {},
|
|
|
|
|
|
|
|
twitterUrl: 'https://twitter.com/Wappalyzer',
|
|
|
|
version: '',
|
|
|
|
version: '',
|
|
|
|
|
|
|
|
|
|
|
|
init: function() {
|
|
|
|
init: function() {
|
|
|
@ -42,10 +43,10 @@ var wappalyzer = (function() {
|
|
|
|
self.prefs.addObserver('', wappalyzer, false);
|
|
|
|
self.prefs.addObserver('', wappalyzer, false);
|
|
|
|
|
|
|
|
|
|
|
|
self.showApps = self.prefs.getIntPref( 'showApps');
|
|
|
|
self.showApps = self.prefs.getIntPref( 'showApps');
|
|
|
|
self.autoDetect = self.prefs.getBoolPref('autoDetect');
|
|
|
|
|
|
|
|
self.customApps = self.prefs.getCharPref('customApps');
|
|
|
|
self.customApps = self.prefs.getCharPref('customApps');
|
|
|
|
self.debug = self.prefs.getBoolPref('debug');
|
|
|
|
self.debug = self.prefs.getBoolPref('debug');
|
|
|
|
self.enableTracking = self.prefs.getBoolPref('enableTracking');
|
|
|
|
self.enableTracking = self.prefs.getBoolPref('enableTracking');
|
|
|
|
|
|
|
|
self.popupOnHover = self.prefs.getBoolPref('popupOnHover');
|
|
|
|
self.newInstall = self.prefs.getBoolPref('newInstall');
|
|
|
|
self.newInstall = self.prefs.getBoolPref('newInstall');
|
|
|
|
self.version = self.prefs.getCharPref('version');
|
|
|
|
self.version = self.prefs.getCharPref('version');
|
|
|
|
|
|
|
|
|
|
|
@ -57,28 +58,28 @@ var wappalyzer = (function() {
|
|
|
|
|
|
|
|
|
|
|
|
self.moveLocation(locationPref);
|
|
|
|
self.moveLocation(locationPref);
|
|
|
|
|
|
|
|
|
|
|
|
// Open page after upgrade
|
|
|
|
// Open page after installation
|
|
|
|
try {
|
|
|
|
if ( self.newInstall ) {
|
|
|
|
var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
|
|
|
|
self.prefs.setBoolPref('newInstall', false);
|
|
|
|
|
|
|
|
|
|
|
|
var enabledItems = prefs.getCharPref('extensions.enabledAddons');
|
|
|
|
gBrowser.addEventListener('DOMContentLoaded', self.installSuccess, false);
|
|
|
|
var version = enabledItems.replace(/(^.*wappalyzer[^:]+:)([^,]+),.*$/, '$2');
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// Open page after upgrade
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
|
|
|
|
|
|
|
|
|
|
|
|
if ( version && self.version != version ) {
|
|
|
|
var enabledItems = prefs.getCharPref('extensions.enabledAddons');
|
|
|
|
self.browser.addEventListener('load', self.upgradeSuccess, false);
|
|
|
|
var version = enabledItems.replace(/(^.*wappalyzer[^:]+:)([^,]+),.*$/, '$2');
|
|
|
|
|
|
|
|
|
|
|
|
self.version = version;
|
|
|
|
if ( version && self.version != version ) {
|
|
|
|
|
|
|
|
gBrowser.addEventListener('DOMContentLoaded', self.upgradeSuccess, false);
|
|
|
|
|
|
|
|
|
|
|
|
self.prefs.setCharPref('version', self.version);
|
|
|
|
self.version = version;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
catch(e) { }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Open page after installation
|
|
|
|
self.prefs.setCharPref('version', self.version);
|
|
|
|
if ( self.newInstall ) {
|
|
|
|
}
|
|
|
|
self.prefs.setBoolPref('newInstall', false);
|
|
|
|
}
|
|
|
|
|
|
|
|
catch(e) { }
|
|
|
|
self.browser.addEventListener('load', self.installSuccess, false);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Listen messages sent from the content process
|
|
|
|
// Listen messages sent from the content process
|
|
|
@ -113,10 +114,6 @@ var wappalyzer = (function() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch(data) {
|
|
|
|
switch(data) {
|
|
|
|
case 'autoDetect':
|
|
|
|
|
|
|
|
self.autoDetect = self.prefs.getBoolPref('autoDetect');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'customApps':
|
|
|
|
case 'customApps':
|
|
|
|
self.customApps = self.prefs.getCharPref('customApps');
|
|
|
|
self.customApps = self.prefs.getCharPref('customApps');
|
|
|
|
|
|
|
|
|
|
|
@ -128,6 +125,12 @@ var wappalyzer = (function() {
|
|
|
|
case 'enableTracking':
|
|
|
|
case 'enableTracking':
|
|
|
|
self.enableTracking = self.prefs.getBoolPref('enableTracking');
|
|
|
|
self.enableTracking = self.prefs.getBoolPref('enableTracking');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'popupOnHover':
|
|
|
|
|
|
|
|
self.popupOnHover = self.prefs.getBoolPref('popupOnHover');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.moveLocation();
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case 'showApps':
|
|
|
|
case 'showApps':
|
|
|
|
self.showApps = self.prefs.getIntPref('showApps');
|
|
|
|
self.showApps = self.prefs.getIntPref('showApps');
|
|
|
@ -192,6 +195,16 @@ var wappalyzer = (function() {
|
|
|
|
var e = document.getElementById(containerId);
|
|
|
|
var e = document.getElementById(containerId);
|
|
|
|
var container = document.getElementById('wappalyzer-container');
|
|
|
|
var container = document.getElementById('wappalyzer-container');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ( self.popupOnHover ) {
|
|
|
|
|
|
|
|
container.addEventListener('mouseover', function() {
|
|
|
|
|
|
|
|
self.hoverTimeout = setTimeout(function() {
|
|
|
|
|
|
|
|
document.getElementById('wappalyzer-apps').openPopup(document.getElementById('wappalyzer-container'), 'after_end');
|
|
|
|
|
|
|
|
}, 200);
|
|
|
|
|
|
|
|
}, false);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
container.addEventListener('mouseout', function() { clearTimeout(self.hoverTimeout); }, false);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
e.appendChild(container);
|
|
|
|
e.appendChild(container);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
@ -210,8 +223,7 @@ var wappalyzer = (function() {
|
|
|
|
target.documentElement.innerHTML,
|
|
|
|
target.documentElement.innerHTML,
|
|
|
|
[],
|
|
|
|
[],
|
|
|
|
[],
|
|
|
|
[],
|
|
|
|
true,
|
|
|
|
true
|
|
|
|
false
|
|
|
|
|
|
|
|
);
|
|
|
|
);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
@ -224,8 +236,7 @@ var wappalyzer = (function() {
|
|
|
|
message.json.html,
|
|
|
|
message.json.html,
|
|
|
|
message.json.headers,
|
|
|
|
message.json.headers,
|
|
|
|
message.json.environmentVars,
|
|
|
|
message.json.environmentVars,
|
|
|
|
true,
|
|
|
|
true
|
|
|
|
false
|
|
|
|
|
|
|
|
);
|
|
|
|
);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
@ -246,7 +257,6 @@ var wappalyzer = (function() {
|
|
|
|
doc.documentElement ? doc.documentElement.innerHTML : '',
|
|
|
|
doc.documentElement ? doc.documentElement.innerHTML : '',
|
|
|
|
[],
|
|
|
|
[],
|
|
|
|
[],
|
|
|
|
[],
|
|
|
|
false,
|
|
|
|
|
|
|
|
false
|
|
|
|
false
|
|
|
|
);
|
|
|
|
);
|
|
|
|
},
|
|
|
|
},
|
|
|
@ -284,7 +294,7 @@ var wappalyzer = (function() {
|
|
|
|
onSecurityChange: function(a, b, c) {}
|
|
|
|
onSecurityChange: function(a, b, c) {}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
analyzePage: function(doc, href, html, headers, environmentVars, doCount, manualDetect) {
|
|
|
|
analyzePage: function(doc, href, html, headers, environmentVars, doCount) {
|
|
|
|
self.log('analyzePage');
|
|
|
|
self.log('analyzePage');
|
|
|
|
|
|
|
|
|
|
|
|
self.currentTab = false;
|
|
|
|
self.currentTab = false;
|
|
|
@ -303,83 +313,81 @@ var wappalyzer = (function() {
|
|
|
|
html = '';
|
|
|
|
html = '';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( self.autoDetect || ( !self.autoDetect && manualDetect ) ) {
|
|
|
|
// Prevent large documents from slowing things down
|
|
|
|
// Prevent large documents from slowing things down
|
|
|
|
if ( html.length > 50000 ) {
|
|
|
|
if ( html.length > 50000 ) {
|
|
|
|
html = html.substring(0, 25000) + html.substring(html.length - 25000, html.length);
|
|
|
|
html = html.substring(0, 25000) + html.substring(html.length - 25000, html.length);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Scan URL, domain and response headers for patterns
|
|
|
|
// Scan URL, domain and response headers for patterns
|
|
|
|
if ( html ) {
|
|
|
|
if ( html || self.request ) {
|
|
|
|
// Check cached application names
|
|
|
|
// Check cached application names
|
|
|
|
if ( doc && typeof doc.detectedApps != 'undefined' ) {
|
|
|
|
if ( doc && typeof doc.detectedApps != 'undefined' ) {
|
|
|
|
for ( i in doc.detectedApps ) {
|
|
|
|
for ( i in doc.detectedApps ) {
|
|
|
|
var appName = doc.detectedApps[i];
|
|
|
|
var appName = doc.detectedApps[i];
|
|
|
|
|
|
|
|
|
|
|
|
if ( typeof self.checkUnique[appName] == 'undefined' ) {
|
|
|
|
if ( typeof self.checkUnique[appName] == 'undefined' ) {
|
|
|
|
self.log('CACHE'); //
|
|
|
|
self.log('CACHE'); //
|
|
|
|
|
|
|
|
|
|
|
|
self.showApp(appName, doc, href, doCount);
|
|
|
|
self.showApp(appName, doc, href, doCount);
|
|
|
|
|
|
|
|
|
|
|
|
self.checkUnique[appName] = true;
|
|
|
|
self.checkUnique[appName] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for ( var appName in self.apps ) {
|
|
|
|
for ( var appName in self.apps ) {
|
|
|
|
// Don't scan for apps that have already been detected
|
|
|
|
// Don't scan for apps that have already been detected
|
|
|
|
if ( typeof self.checkUnique[appName] == 'undefined' ) {
|
|
|
|
if ( typeof self.checkUnique[appName] == 'undefined' ) {
|
|
|
|
// Scan HTML
|
|
|
|
// Scan HTML
|
|
|
|
if ( typeof self.apps[appName].html != 'undefined' ) {
|
|
|
|
if ( html && typeof self.apps[appName].html != 'undefined' ) {
|
|
|
|
var regex = self.apps[appName].html;
|
|
|
|
var regex = self.apps[appName].html;
|
|
|
|
|
|
|
|
|
|
|
|
if ( regex.test(html) ) {
|
|
|
|
if ( regex.test(html) ) {
|
|
|
|
self.showApp(appName, doc, href, doCount);
|
|
|
|
self.showApp(appName, doc, href, doCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Scan URL
|
|
|
|
// Scan URL
|
|
|
|
if ( href && typeof self.apps[appName].url != 'undefined' ) {
|
|
|
|
if ( href && typeof self.apps[appName].url != 'undefined' ) {
|
|
|
|
var regex = self.apps[appName].url;
|
|
|
|
var regex = self.apps[appName].url;
|
|
|
|
|
|
|
|
|
|
|
|
if ( regex.test(href) ) {
|
|
|
|
if ( regex.test(href) ) {
|
|
|
|
self.showApp(appName, doc, href, doCount);
|
|
|
|
self.showApp(appName, doc, href, doCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Scan response headers
|
|
|
|
// Scan response headers
|
|
|
|
if ( typeof self.apps[appName].headers != 'undefined' && self.request ) {
|
|
|
|
if ( typeof self.apps[appName].headers != 'undefined' && self.request ) {
|
|
|
|
for ( var header in self.apps[appName].headers ) {
|
|
|
|
for ( var header in self.apps[appName].headers ) {
|
|
|
|
var regex = self.apps[appName].headers[header];
|
|
|
|
var regex = self.apps[appName].headers[header];
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
if ( regex.test(self.request.nsIHttpChannel.getResponseHeader(header)) ) {
|
|
|
|
if ( regex.test(self.request.nsIHttpChannel.getResponseHeader(header)) ) {
|
|
|
|
self.showApp(appName, doc, href, doCount);
|
|
|
|
self.showApp(appName, doc, href, doCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch(e) { }
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
catch(e) { }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Scan environment variables
|
|
|
|
// Scan environment variables
|
|
|
|
if ( environmentVars && typeof self.apps[appName].env != 'undefined' ) {
|
|
|
|
if ( environmentVars && typeof self.apps[appName].env != 'undefined' ) {
|
|
|
|
var regex = self.apps[appName].env;
|
|
|
|
var regex = self.apps[appName].env;
|
|
|
|
|
|
|
|
|
|
|
|
for ( var i in environmentVars ) {
|
|
|
|
for ( var i in environmentVars ) {
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
if ( regex.test(environmentVars[i]) ) {
|
|
|
|
if ( regex.test(environmentVars[i]) ) {
|
|
|
|
self.showApp(appName, doc, href, doCount);
|
|
|
|
self.showApp(appName, doc, href, doCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch(e) { }
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
catch(e) { }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
html = ''; // Free memory
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
html = ''; // Free memory
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
showApp: function(detectedApp, doc, href, doCount) {
|
|
|
|
showApp: function(detectedApp, doc, href, doCount) {
|
|
|
@ -410,38 +418,36 @@ var wappalyzer = (function() {
|
|
|
|
if ( show && self.currentTab ) {
|
|
|
|
if ( show && self.currentTab ) {
|
|
|
|
var e = document.getElementById('wappalyzer-detected-apps');
|
|
|
|
var e = document.getElementById('wappalyzer-detected-apps');
|
|
|
|
|
|
|
|
|
|
|
|
if ( self.autoDetect ) {
|
|
|
|
if ( self.showApps == 2 ) {
|
|
|
|
if ( self.showApps == 2 ) {
|
|
|
|
document.getElementById('wappalyzer-icon').setAttribute('src', 'chrome://wappalyzer/skin/icon16x16_hot.ico');
|
|
|
|
document.getElementById('wappalyzer-icon').setAttribute('src', 'chrome://wappalyzer/skin/icon16x16_hot.ico');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById('wappalyzer-detected-apps').style.display = 'none';
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
// Hide Wappalyzer icon
|
|
|
|
|
|
|
|
document.getElementById('wappalyzer-icon').style.display = 'none';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById('wappalyzer-detected-apps').style.display = '';
|
|
|
|
document.getElementById('wappalyzer-detected-apps').style.display = 'none';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
// Hide Wappalyzer icon
|
|
|
|
|
|
|
|
document.getElementById('wappalyzer-icon').style.display = 'none';
|
|
|
|
|
|
|
|
|
|
|
|
// Show app icon and label
|
|
|
|
document.getElementById('wappalyzer-detected-apps').style.display = '';
|
|
|
|
var child = document.createElement('image');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( typeof self.apps[detectedApp].icon == 'string' ) {
|
|
|
|
// Show app icon and label
|
|
|
|
child.setAttribute('src', self.apps[detectedApp].icon);
|
|
|
|
var child = document.createElement('image');
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
child.setAttribute('src', 'chrome://wappalyzer/skin/icons/' + detectedApp + '.ico');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
child.setAttribute('class', 'wappalyzer-icon');
|
|
|
|
if ( typeof self.apps[detectedApp].icon == 'string' ) {
|
|
|
|
|
|
|
|
child.setAttribute('src', self.apps[detectedApp].icon);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
child.setAttribute('src', 'chrome://wappalyzer/skin/icons/' + detectedApp + '.ico');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( self.appsDetected ) {
|
|
|
|
child.setAttribute('class', 'wappalyzer-icon');
|
|
|
|
child.setAttribute('style', 'margin-left: .5em');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
e.appendChild(child);
|
|
|
|
if ( self.appsDetected ) {
|
|
|
|
|
|
|
|
child.setAttribute('style', 'margin-left: .5em');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
e.appendChild(child);
|
|
|
|
|
|
|
|
|
|
|
|
if ( self.showApps == 0 ) {
|
|
|
|
if ( self.showApps == 0 ) {
|
|
|
|
var child = document.createElement('label');
|
|
|
|
var child = document.createElement('label');
|
|
|
|
|
|
|
|
|
|
|
@ -622,7 +628,7 @@ var wappalyzer = (function() {
|
|
|
|
installSuccess: function() {
|
|
|
|
installSuccess: function() {
|
|
|
|
self.log('installSuccess');
|
|
|
|
self.log('installSuccess');
|
|
|
|
|
|
|
|
|
|
|
|
self.browser.removeEventListener('load', self.installSuccess, false);
|
|
|
|
gBrowser.removeEventListener('DOMContentLoaded', self.installSuccess, false);
|
|
|
|
|
|
|
|
|
|
|
|
self.openTab(self.homeUrl + 'install/success/');
|
|
|
|
self.openTab(self.homeUrl + 'install/success/');
|
|
|
|
},
|
|
|
|
},
|
|
|
@ -630,7 +636,7 @@ var wappalyzer = (function() {
|
|
|
|
upgradeSuccess: function() {
|
|
|
|
upgradeSuccess: function() {
|
|
|
|
self.log('upgradeSuccess');
|
|
|
|
self.log('upgradeSuccess');
|
|
|
|
|
|
|
|
|
|
|
|
self.browser.removeEventListener('load', self.upgradeSuccess, false);
|
|
|
|
gBrowser.removeEventListener('DOMContentLoaded', self.upgradeSuccess, false);
|
|
|
|
|
|
|
|
|
|
|
|
self.openTab(self.homeUrl + 'install/upgraded/');
|
|
|
|
self.openTab(self.homeUrl + 'install/upgraded/');
|
|
|
|
}
|
|
|
|
}
|
|
|
|