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.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

676 lines
18 KiB

15 years ago
// Wappalyzer by ElbertF 2009 http://elbertf.com
var wappalyzer = (function() {
var self = {
apps: {},
appsDetected: 0,
browser: false,
cats: {},
checkUnique: {},
currentTab: false,
customApps: '',
debug: false,
enableTracking: true,
githubUrl: 'https://github.com/ElbertF/Wappalyzer',
history: {},
hitCount: 0,
homeUrl: 'http://wappalyzer.com/',
hoverTimeout: false,
newInstall: false,
popupOnHover: true,
prevUrl: '',
prefs: {},
regexBlacklist: /(dev\.|\/admin|\.local)/,
regexDomain: /^[a-z0-9._\-]+\.[a-z]+/,
req: false,
request: false,
showApps: 1,
showCats: [],
strings: {},
twitterUrl: 'https://twitter.com/Wappalyzer',
version: '',
init: function() {
self.log('init');
self.browser = gBrowser;
self.strings = document.getElementById('wappalyzer-strings');
// Preferences
self.prefs = Components.classes['@mozilla.org/preferences-service;1'].getService(Components.interfaces.nsIPrefService).getBranch('wappalyzer.');
self.prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);
self.prefs.addObserver('', wappalyzer, false);
self.showApps = self.prefs.getIntPref( 'showApps');
self.customApps = self.prefs.getCharPref('customApps');
self.debug = self.prefs.getBoolPref('debug');
self.enableTracking = self.prefs.getBoolPref('enableTracking');
self.popupOnHover = self.prefs.getBoolPref('popupOnHover');
self.newInstall = self.prefs.getBoolPref('newInstall');
self.version = self.prefs.getCharPref('version');
var i = 0;
while ( ++ i ) {
try {
self.showCats[i] = self.prefs.getBoolPref('cat' + i);
} catch (e) {
break;
}
}
var locationPref = self.prefs.getIntPref('location');
self.moveLocation(locationPref);
// Open page after installation
if ( self.newInstall ) {
self.prefs.setBoolPref('newInstall', false);
14 years ago
gBrowser.addEventListener('DOMContentLoaded', self.installSuccess, false);
} else {
// Open page after upgrade
try {
var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
14 years ago
var enabledItems = prefs.getCharPref('extensions.enabledAddons');
var version = enabledItems.replace(/(^.*wappalyzer[^:]+:)([^,]+),.*$/, '$2');
14 years ago
if ( version && self.version != version ) {
gBrowser.addEventListener('DOMContentLoaded', self.upgradeSuccess, false);
14 years ago
self.version = version;
14 years ago
self.prefs.setCharPref('version', self.version);
}
}
catch(e) { }
}
15 years ago
// Listen messages sent from the content process
if ( typeof messageManager != 'undefined' ) {
messageManager.addMessageListener('wappalyzer:onPageLoad', self.onContentPageLoad);
messageManager.loadFrameScript('chrome://wappalyzer/content/content.js', true);
}
// Listen for URL changes
self.browser.addProgressListener(self.urlChange, Components.interfaces.nsIWebProgress.NOTIFY_LOCATION);
15 years ago
// Listen for page loads
self.browser.addEventListener('DOMContentLoaded', self.onPageLoad, true);
self.evaluateCustomApps();
},
15 years ago
// Log messages to console
log: function(message) {
if ( self.debug && message ) {
var consoleService = Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService);
14 years ago
consoleService.logStringMessage("Wappalyzer: " + message);
}
},
15 years ago
// Listen for preference changes
observe: function(subject, topic, data) {
if ( topic != 'nsPref:changed' ) {
return;
}
switch(true) {
case data == 'customApps':
self.customApps = self.prefs.getCharPref('customApps');
14 years ago
break;
case data == 'debug':
self.debug = self.prefs.getBoolPref('debug');
break;
case data == 'enableTracking':
self.enableTracking = self.prefs.getBoolPref('enableTracking');
break;
case data == 'popupOnHover':
self.popupOnHover = self.prefs.getBoolPref('popupOnHover');
self.moveLocation();
break;
case data == 'showApps':
self.showApps = self.prefs.getIntPref('showApps');
break;
case data == 'location':
var locationPref = self.prefs.getIntPref('location');
self.moveLocation(locationPref);
break;
case data.test(/^cat[0-9]+$/):
var cat = data.replace(/^cat([0-9]+)$/, '$1');
self.showCats[cat] = self.prefs.getIntPref('cat' + cat);
break;
}
},
openTab: function(url) {
self.browser.selectedTab = self.browser.addTab(url);
},
15 years ago
moveLocation: function(locationPref) {
self.log('moveLocation');
switch ( locationPref ) {
case 1:
var containerId = 'wappalyzer-statusbar';
15 years ago
// Show status bar panel
document.getElementById('wappalyzer-statusbar').style.visibility = '';
document.getElementById('wappalyzer-statusbar').style.padding = '1px';
break;
default:
var containerId = 'urlbar-icons';
// Hide status bar panel
document.getElementById('wappalyzer-statusbar').style.visibility = 'hidden';
document.getElementById('wappalyzer-statusbar').style.padding = '0';
15 years ago
}
var e = document.getElementById(containerId);
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);
15 years ago
},
onPageLoad: function(event) {
self.log('onPageLoad');
var target = event.originalTarget;
if ( !target.request ) {
self.request = false;
}
self.analyzePage(
target.documentElement,
target.location.href,
target.documentElement.innerHTML,
[],
[],
true
);
},
onContentPageLoad: function(message) {
self.log('onContentPageLoad');
self.analyzePage(
null,
message.json.href,
message.json.html,
message.json.headers,
message.json.environmentVars,
true
);
15 years ago
},
onUrlChange: function(request) {
self.log('onUrlChange');
15 years ago
self.clearDetectedApps();
var doc = self.browser.contentDocument;
15 years ago
self.request = doc.request ? doc.request : request;
self.currentTab = false;
15 years ago
self.analyzePage(
doc,
doc.location.href ? doc.location.href : '',
doc.documentElement ? doc.documentElement.innerHTML : '',
[],
[],
false
);
},
urlChange: {
QueryInterface: function(iid) {
if ( iid.equals(Components.interfaces.nsIWebProgressListener) ||
iid.equals(Components.interfaces.nsISupportsWeakReference) ||
iid.equals(Components.interfaces.nsISupports) ) {
return this;
}
throw Components.results.NS_NOINTERFACE;
},
onLocationChange: function(progress, request, url) {
self.log('urlChange.onLocationChange');
if ( !url ) {
self.prevUrl = '';
return;
}
if ( url.spec != self.prevUrl ) {
self.prevUrl = url.spec;
self.onUrlChange(request);
}
},
onStateChange: function(a, b, c, d) {},
onProgressChange: function(a, b, c, d, e, f) {},
onStatusChange: function(a, b, c, d) {},
onSecurityChange: function(a, b, c) {}
},
analyzePage: function(doc, href, html, headers, environmentVars, doCount) {
self.log('analyzePage');
self.currentTab = false;
if ( href == self.browser.contentDocument.location.href ) {
self.currentTab = true;
if ( !doc ) {
doc = self.browser.contentDocument;
}
self.clearDetectedApps();
}
if ( typeof html == 'undefined' ) {
html = '';
}
// Prevent large documents from slowing things down
if ( html.length > 50000 ) {
html = html.substring(0, 25000) + html.substring(html.length - 25000, html.length);
}
// Scan URL, domain and response headers for patterns
if ( html || self.request ) {
// Check cached application names
if ( doc && typeof doc.detectedApps != 'undefined' ) {
for ( i in doc.detectedApps ) {
var appName = doc.detectedApps[i];
if ( typeof self.checkUnique[appName] == 'undefined' ) {
self.log('CACHE'); //
self.showApp(appName, doc, href, doCount);
self.checkUnique[appName] = true;
}
}
}
for ( var appName in self.apps ) {
// Don't scan for apps that have already been detected
if ( typeof self.checkUnique[appName] == 'undefined' ) {
// Scan HTML
if ( html && typeof self.apps[appName].html != 'undefined' ) {
if ( self.apps[appName].html.test(html) ) {
self.showApp(appName, doc, href, doCount);
}
}
// Scan script tags
if ( html && typeof self.apps[appName].script != 'undefined' ) {
var
regex = /<script[^>]+src=("|')([^"']+)\1/ig,
match = []
;
while ( match = regex.exec(html) ) {
if ( self.apps[appName].script.test(match[2]) ) {
self.showApp(appName, doc, href, doCount);
break;
}
}
}
// Scan meta tags
if ( html && typeof self.apps[appName].meta != 'undefined' ) {
var
regex = /<meta[^>]+>/ig,
match = []
;
while ( match = regex.exec(html) ) {
for ( meta in self.apps[appName].meta ) {
if ( new RegExp('name=["\']' + meta + '["\']', 'i').test(match) ) {
var content = match.toString().match(/content=("|')([^"']+)("|')/i);
if ( self.apps[appName].meta[meta].test(content[2]) ) {
self.showApp(appName, doc, href, doCount);
break;
}
}
}
}
}
// Scan URL
if ( href && typeof self.apps[appName].url != 'undefined' ) {
var regex = self.apps[appName].url;
if ( regex.test(href) ) {
self.showApp(appName, doc, href, doCount);
15 years ago
}
}
// Scan response headers
if ( typeof self.apps[appName].headers != 'undefined' && self.request ) {
for ( var header in self.apps[appName].headers ) {
var regex = self.apps[appName].headers[header];
try {
if ( regex.test(self.request.nsIHttpChannel.getResponseHeader(header)) ) {
self.showApp(appName, doc, href, doCount);
}
}
catch(e) { }
15 years ago
}
}
// Scan environment variables
if ( environmentVars && typeof self.apps[appName].env != 'undefined' ) {
var regex = self.apps[appName].env;
for ( var i in environmentVars ) {
try {
if ( regex.test(environmentVars[i]) ) {
self.showApp(appName, doc, href, doCount);
}
}
catch(e) { }
}
}
15 years ago
}
}
}
html = ''; // Free memory
},
15 years ago
showApp: function(detectedApp, doc, href, doCount) {
self.log('showApp ' + detectedApp);
self.report(detectedApp, href);
// Keep detected application names in memory
if ( doc ) {
if ( typeof doc.detectedApps == 'undefined' ) {
doc.detectedApps = [];
}
doc.detectedApps.push(detectedApp);
}
if ( detectedApp && typeof self.checkUnique[detectedApp] == 'undefined' ) {
var show = false;
for ( var i in self.apps[detectedApp].cats ) {
if ( self.showCats[self.apps[detectedApp].cats[i]] ) {
show = true;
15 years ago
break;
}
}
15 years ago
if ( show && self.currentTab ) {
var e = document.getElementById('wappalyzer-detected-apps');
if ( self.showApps == 2 ) {
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 = '';
}
// Show app icon and label
var child = document.createElement('image');
if ( typeof self.apps[detectedApp].icon == 'string' ) {
child.setAttribute('src', self.apps[detectedApp].icon);
}
else {
child.setAttribute('src', 'chrome://wappalyzer/skin/icons/' + detectedApp + '.ico');
}
14 years ago
child.setAttribute('class', 'wappalyzer-icon');
if ( self.appsDetected ) {
child.setAttribute('style', 'margin-left: .5em');
}
e.appendChild(child);
if ( self.showApps == 0 ) {
var child = document.createElement('label');
child.setAttribute('value', detectedApp);
child.setAttribute('class', 'wappalyzer-app-name');
e.appendChild(child);
}
15 years ago
// Show application in popup
var e = document.getElementById('wappalyzer-apps');
15 years ago
if ( !self.appsDetected ) {
// Remove "no apps detected" message
document.getElementById('wappalyzer-apps').removeChild(document.getElementById('wappalyzer-no-detected-apps'));
14 years ago
}
else {
var child = document.createElement('menuseparator');
e.appendChild(child);
14 years ago
}
15 years ago
var child = document.createElement('menuitem');
child.setAttribute('class', 'menuitem-iconic');
child.setAttribute('type', '');
child.addEventListener('command', function() { self.openTab(self.homeUrl + 'stats/app/' + escape(detectedApp)); }, false);
if ( typeof self.apps[detectedApp].custom == 'undefined' ) {
child.setAttribute('label', detectedApp);
child.setAttribute('image', 'chrome://wappalyzer/skin/icons/' + detectedApp + '.ico');
}
else {
child.setAttribute('label', detectedApp + ' (' + self.strings.getString('wappalyzer.custom') + ')');
child.setAttribute('disabled', 'true');
child.setAttribute('image', self.apps[detectedApp].icon);
14 years ago
}
15 years ago
14 years ago
e.appendChild(child);
15 years ago
if ( self.apps[detectedApp].cats ) {
for ( var i in self.apps[detectedApp].cats ) {
var child = document.createElement('menuitem');
15 years ago
child.setAttribute('label', self.cats[self.apps[detectedApp].cats[i]].name);
child.setAttribute('disabled', 'true');
15 years ago
e.appendChild(child);
}
}
14 years ago
}
15 years ago
if ( doCount ) {
self.report(detectedApp, href);
14 years ago
}
self.appsDetected ++;
15 years ago
self.checkUnique[detectedApp] = true;
}
},
15 years ago
report: function(detectedApp, href) {
self.log('report');
15 years ago
if ( typeof self.apps[detectedApp].custom == 'undefined' ) {
var
regex = /:\/\/(.[^/]+)/,
domain = href.match(regex) ? href.match(regex)[1] : ''
;
15 years ago
if ( self.enableTracking && self.regexDomain.test(domain) && !self.regexBlacklist.test(href) ) {
if ( typeof self.history[domain] == 'undefined' ) {
self.history[domain] = [];
}
if ( typeof self.history[domain][detectedApp] == 'undefined' ) {
self.history[domain][detectedApp] = 0;
}
self.history[domain][detectedApp] ++;
self.hitCount ++;
if ( self.hitCount > 200 ) {
self.sendReport();
14 years ago
}
}
}
},
// Anonymously send the name of the detected apps and domains to wappalyzer.com
// You can turn this off in the options dialog
// This is used to track the distribution of software, stats are publicly available on the site
sendReport: function() {
self.log('sendReport');
15 years ago
if ( self.enableTracking && !self.req ) {
var report = '';
15 years ago
if ( self.history ) {
for ( var i in self.history ) {
report += '[' + i;
15 years ago
for ( var j in self.history[i] ) {
report += '|' + j + ':' + self.history[i][j];
}
report += ']';
}
14 years ago
}
15 years ago
// Make POST request
self.req = new XMLHttpRequest();
15 years ago
self.req.open('POST', self.homeUrl + 'report/', true);
14 years ago
self.req.channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE;
15 years ago
self.req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
15 years ago
self.req.onreadystatechange = function(e) {
if ( self.req.readyState == 4 ) {
if ( self.req.status == 200 ) {
// Reset
report = '';
15 years ago
self.hitCount = 0;
self.history = [];
}
15 years ago
self.req.close();
15 years ago
self.req = false;
}
};
15 years ago
self.req.send('d=' + encodeURIComponent(report));
}
},
15 years ago
clearDetectedApps: function() {
self.log('clearDetectedApps');
15 years ago
self.appsDetected = 0;
self.checkUnique = [];
// Show Wappalyzer icon
document.getElementById('wappalyzer-icon').setAttribute('src', 'chrome://wappalyzer/skin/icon16x16.ico');
document.getElementById('wappalyzer-icon').style.display = '';
15 years ago
// Clear app icons and labels
var e = document.getElementById('wappalyzer-detected-apps');
15 years ago
while ( e.childNodes.length > 0 ) {
e.removeChild(e.childNodes.item(0));
}
// Clear application popup
var e = document.getElementById('wappalyzer-apps');
15 years ago
while ( e.childNodes.length > 0 ) {
e.removeChild(e.childNodes.item(0));
}
15 years ago
var child = document.createElement('menuitem');
15 years ago
child.setAttribute('label', self.strings.getString('wappalyzer.noDetectedApps'));
child.setAttribute('id', 'wappalyzer-no-detected-apps');
child.setAttribute('class', 'menuitem-iconic');
child.setAttribute('disabled', 'true');
child.setAttribute('type', '');
e.appendChild(child);
},
15 years ago
installSuccess: function() {
self.log('installSuccess');
15 years ago
gBrowser.removeEventListener('DOMContentLoaded', self.installSuccess, false);
self.openTab(self.homeUrl + 'install/success/');
},
14 years ago
upgradeSuccess: function() {
self.log('upgradeSuccess');
15 years ago
gBrowser.removeEventListener('DOMContentLoaded', self.upgradeSuccess, false);
self.openTab(self.homeUrl + 'install/upgraded/');
}
};
addEventListener('load', function() { self.init(); }, false);
addEventListener('unload', function() { self.sendReport(); }, false);
14 years ago
return self;
})();