1#!/usr/bin/ruby
2
3# Copyright (c) 2012 Apple Inc. All Rights Reserved.
4
5# IMPORTANT NOTE: This file is licensed only for use on Apple-branded
6# computers and is subject to the terms and conditions of the Apple Software
7# License Agreement accompanying the package this file is a part of.
8# You may not port this file to another platform without Apple's written consent.
9
10# This is a wrapper script for httpd, intended to gather minimal usage data on
11# the Apache web server. This script logs usage data locally, but that data is only
12# sent to Apple if the "send usage data" option is turned on.
13
14begin
15	LastUseFile = "/var/db/.httpd-wrapper"
16	TooSoonInSeconds = 3600.0		# Throttle
17	if !FileTest.exists?(LastUseFile) || Time.now - File.new(LastUseFile).atime > TooSoonInSeconds
18		require 'fileutils'
19		FileUtils.touch(LastUseFile)
20		$SERVER_LIBRARY_PATH = '/Library/Server'
21		$SERVER_WEB_CONFIG_DIR = "#{$SERVER_LIBRARY_PATH}/Web/Config/apache2/"
22		ServerDefaultWebConfigPath = "#{$SERVER_WEB_CONFIG_DIR}httpd_server_app.conf"
23		ServerDefaultWebVHostConfigPath = "#{$SERVER_WEB_CONFIG_DIR}sites/0000_any_80_.conf"
24		ServerDefaultWebSecureVHostConfigPath = "#{$SERVER_WEB_CONFIG_DIR}sites/0000_any_443_.conf"
25		DesktopDefaultWebConfigPath = '/etc/apache2/httpd.conf'
26		ServerAppPath = '/Applications/Server.app/Contents/MacOS/Server'
27		ServerWordPressPath = "#{$SERVER_LIBRARY_PATH}/Web/Data/Sites/Default/wordpress"
28		DesktopWordPressPath = '/Library/WebServer/Documents/wordpress'
29		ThinPath = '/usr/bin/thin'
30		MySQLPath = '/usr/local/mysql'
31		MampPath = '/Applications/MAMP'
32		XCodePath = '/Applications/Xcode.app'
33		activeConfigFile = DesktopDefaultWebConfigPath
34		config_dict = {
35			'active' => 0,
36			'valid_syntax' => 0,
37			'invalid_syntax' => 0,
38			'invalid_usage' => 0,
39			'failed_open' => 0,
40			'unexpected_response' => 0,
41			'zero_exit_status' => 0,
42			'nonzero_exit_status' => 0,
43			'has_php_enabled' => 0,
44			'has_perl_enabled' => 0,
45			'has_ssl_enabled' => 0,
46			'has_third_party_web_sw' => 0,
47			'has_xcode' => 0
48		}
49		settings = {
50			'apache_launch' => 1,
51			'desktop_config' => config_dict.clone,
52			'server_config' => config_dict.clone,
53			'unknown_config' => config_dict.clone,
54			'has_server_app_installed' => 0,
55			'uses_pristine_desktop_config_file' => 0,
56			'uses_pristine_server_main_config_file' => 0,
57			'uses_pristine_server_vhost_config_file' => 0,
58			'has_server_webservice_enabled' => 0,
59			'has_wiki_enabled' => 0,
60			'has_wsgi_enabled' => 0,
61			'has_devicemanagement_enabled' => 0,
62			'has_calendarserver_enabled' => 0,
63			'has_webdavsharing_enabled' => 0,
64			'has_custom_sites' => 0,
65			'has_many_custom_sites' => 0
66		}
67		for i in 0..ARGV.count - 2
68			if ARGV[i] == "-f"
69				activeConfigFile = ARGV[i + 1]
70			elsif ARGV[i] == "-D" && ARGV[i + 1] == "WEBSERVICE_ON"
71				settings['has_server_webservice_enabled'] = 1
72			end
73		end
74		sum = `/usr/bin/cksum #{activeConfigFile} 2>&1`.chomp.split(/\s+/)[0]
75		if activeConfigFile == ServerDefaultWebConfigPath
76			config = 'server_config'
77			if ['2471706962'].include?(sum)
78				settings['uses_pristine_server_main_config_file'] = 1
79			end
80		elsif activeConfigFile == DesktopDefaultWebConfigPath
81			config = 'desktop_config'
82			if ['3298336432'].include?(sum)
83				settings['uses_pristine_desktop_config_file'] = 1
84			end
85		else
86			config = 'unknown_config'
87		end
88		settings[config]['active'] = 1
89		open(activeConfigFile) do |file|
90			file.each_line do |line|
91				if line.match(/^\s*LoadModule\s+php5_module/)
92					settings[config]['has_php_enabled'] = 1
93				elsif line.match(/^\s*LoadModule\s+perl_module/)
94					settings[config]['has_perl_enabled'] = 1
95				elsif config == 'server_config' && line.match(/^\s*Include\s+\/private\/etc\/apache2\/extra\/httpd-ssl.conf/)
96					settings[config]['has_ssl_enabled'] = 1
97				elsif config == 'unknown_config' && line.match(/^\s*SSLEngine\s+On/)
98					settings[config]['has_ssl_enabled'] = 1
99				end
100			end
101		end
102		if config == 'server_config'
103			sum = `/usr/bin/cksum #{ServerDefaultWebVHostConfigPath} 2>&1`.chomp.split(/\s+/)[0]
104			if ['872795171', '1914927767'].include?(sum)
105				settings['uses_pristine_server_vhost_config_file'] = 1
106			else
107				open(ServerDefaultWebVHostConfigPath) do |file|
108					file.each_line do |line|
109						if line.match(/^\s*Include\s+.*httpd_wsgi.conf/)
110							settings['has_wsgi_enabled'] = 1
111						elsif line.match(/^\s*Include\s+.*httpd_corecollaboration_required.conf/)
112							settings['has_wiki_enabled'] = 1
113						elsif line.match(/^\s*Include\s+.*httpd_devicemanagement.conf/)
114							settings['has_devicemanagement_enabled'] = 1
115						elsif line.match(/^\s*Include\s+.*httpd_calendarserver.conf/)
116							settings['has_calendarserver_enabled'] = 1
117						elsif line.match(/^\s*Include\s+.*httpd_webdavsharing.conf/)
118							settings['has_webdavsharing_enabled'] = 1
119						end
120					end
121				end
122			end
123			open(ServerDefaultWebSecureVHostConfigPath) do |file|
124				file.each_line do |line|
125					if line.match(/^\s*Include\s+.*httpd_wsgi.conf/)
126						settings['has_wsgi_enabled'] = 1
127					elsif line.match(/^\s*Include\s+.*httpd_corecollaboration_required.conf/)
128						settings['has_wiki_enabled'] = 1
129					elsif line.match(/^\s*Include\s+.*httpd_devicemanagement.conf/)
130						settings['has_devicemanagement_enabled'] = 1
131					elsif line.match(/^\s*Include\s+.*httpd_calendarserver.conf/)
132						settings['has_calendarserver_enabled'] = 1
133					elsif line.match(/^\s*Include\s+.*httpd_webdavsharing.conf/)
134						settings['has_webdavsharing_enabled'] = 1
135					end
136				end
137			end
138			siteCount = Dir.glob("#{$SERVER_WEB_CONFIG_DIR}sites/*.conf").count
139			settings['has_custom_sites'] = 1 if siteCount > 3
140			settings['has_many_custom_sites'] = 1 if siteCount > 12
141		end
142		msg = `/usr/sbin/httpd #{ARGV.join(' ')} -t 2>&1`
143		if msg =~ /^Syntax OK/
144			settings[config]['valid_syntax'] = 1
145		elsif msg =~ /^Syntax error/
146			settings[config]['invalid_syntax'] = 1
147		elsif msg =~ /^Usage:/
148			settings[config]['invalid_usage'] = 1
149		elsif msg =~ /Could not open configuration/
150			settings[config]['failed_open'] = 1
151		else
152			settings[config]['unexpected_response'] = 1
153		end
154		settings[config]['zero_exit_status'] = $?.exitstatus == 0 ? 1 : 0
155		settings[config]['nonzero_exit_status'] = $?.exitstatus == 0 ? 0 : 1
156		settings['has_server_app_installed'] = FileTest.exists?(ServerAppPath) ? 1 : 0
157		settings[config]['has_third_party_web_sw'] = (FileTest.exists?(ServerWordPressPath) || FileTest.exists?(DesktopWordPressPath) \
158													  || FileTest.exists?(MySQLPath) \
159													  || FileTest.exists?(MampPath) \
160													  || FileTest.exists?(ThinPath)) ? 1 : 0
161		settings[config]['has_xcode'] = FileTest.exists?(XCodePath) ? 1 : 0
162
163		`syslog -s -l Notice -k com.apple.message.domain com.apple.server.apache.detailed.launch.stats \
164			com.apple.message.apache_launch #{settings['apache_launch']} \
165	\
166			com.apple.message.uses_server_config #{settings['server_config']['active']} \
167			com.apple.message.server_config_has_valid_syntax #{settings['server_config']['valid_syntax']} \
168			com.apple.message.server_config_has_invalid_syntax #{settings['server_config']['invalid_syntax']} \
169			com.apple.message.server_config_has_invalid_usage #{settings['server_config']['invalid_usage']} \
170			com.apple.message.server_config_has_failed_open #{settings['server_config']['failed_open']} \
171			com.apple.message.server_config_has_unexpected_response #{settings['server_config']['unexpected_response']} \
172			com.apple.message.server_config_has_zero_exit_status #{settings['server_config']['zero_exit_status']} \
173			com.apple.message.server_config_has_nonzero_exit_status #{settings['server_config']['nonzero_exit_status']} \
174	\
175			com.apple.message.uses_desktop_config #{settings['desktop_config']['active']} \
176			com.apple.message.desktop_config_has_valid_syntax #{settings['desktop_config']['valid_syntax']} \
177			com.apple.message.desktop_config_has_invalid_syntax #{settings['desktop_config']['invalid_syntax']} \
178			com.apple.message.desktop_config_has_invalid_usage #{settings['desktop_config']['invalid_usage']} \
179			com.apple.message.desktop_config_has_failed_open #{settings['desktop_config']['failed_open']} \
180			com.apple.message.desktop_config_has_unexpected_response #{settings['desktop_config']['unexpected_response']} \
181			com.apple.message.desktop_config_has_zero_exit_status #{settings['desktop_config']['zero_exit_status']} \
182			com.apple.message.desktop_config_has_nonzero_exit_status #{settings['desktop_config']['nonzero_exit_status']} \
183	\
184			com.apple.message.uses_unknown_config #{settings['unknown_config']['active']} \
185			com.apple.message.unknown_config_has_valid_syntax #{settings['unknown_config']['valid_syntax']} \
186			com.apple.message.unknown_config_has_invalid_syntax #{settings['unknown_config']['invalid_syntax']} \
187	\
188			com.apple.message.uses_pristine_desktop_config_file #{settings['uses_pristine_desktop_config_file']} \
189			com.apple.message.uses_pristine_server_main_config_file #{settings['uses_pristine_server_main_config_file']} \
190			com.apple.message.uses_pristine_server_vhost_config_file #{settings['uses_pristine_server_vhost_config_file']} \
191			com.apple.message.has_server_app_installed #{settings['has_server_app_installed']} \
192			com.apple.message.desktop_config_with_php_enabled #{settings['desktop_config']['has_php_enabled']} \
193			com.apple.message.server_config_with_php_enabled #{settings['server_config']['has_php_enabled']} \
194			com.apple.message.desktop_config_with_perl_enabled #{settings['desktop_config']['has_perl_enabled']} \
195			com.apple.message.server_config_with_perl_enabled #{settings['server_config']['has_perl_enabled']} \
196			com.apple.message.server_third_party_web_sw_installed #{settings['server_config']['has_third_party_web_sw']} \
197			com.apple.message.desktop_third_party_web_sw_installed #{settings['desktop_config']['has_third_party_web_sw']} \
198			com.apple.message.unknown_third_party_web_sw_installed #{settings['unknown_config']['has_third_party_web_sw']} \
199			com.apple.message.server_xcode_installed #{settings['server_config']['has_xcode']} \
200			com.apple.message.desktop_xcode_installed #{settings['desktop_config']['has_xcode']} \
201			com.apple.message.unknown_xcode_installed #{settings['unknown_config']['has_xcode']} \
202			com.apple.message.has_wiki_enabled #{settings['has_wiki_enabled']} \
203			com.apple.message.has_custom_sites #{settings['has_custom_sites']} \
204			com.apple.message.has_many_custom_sites #{settings['has_many_custom_sites']} \
205			com.apple.message.has_wsgi_enabled #{settings['has_wsgi_enabled']} \
206			com.apple.message.has_devicemanagement_enabled #{settings['has_devicemanagement_enabled']} \
207			com.apple.message.has_calendarserver_enabled #{settings['has_calendarserver_enabled']} \
208			com.apple.message.has_webdavsharing_enabled #{settings['has_webdavsharing_enabled']} \
209			com.apple.message.has_ssl_enabled_for_desktop #{settings['desktop_config']['has_ssl_enabled']} \
210			com.apple.message.has_ssl_enabled_for_unknown #{settings['unknown_config']['has_ssl_enabled']} \
211			com.apple.message.has_server_webservice_enabled #{settings['has_server_webservice_enabled']} \
212	\
213			com.apple.message.websites_only #{(settings['has_server_webservice_enabled'] == 1 \
214				&& settings['has_wiki_enabled'] == 0 \
215				&& settings['has_devicemanagement_enabled'] == 0 \
216				&& settings['has_calendarserver_enabled'] == 0 \
217				&& settings['has_webdavsharing_enabled'] == 0) ? 1 : 0} \
218			com.apple.message.websites_or_a_service #{(settings['has_server_webservice_enabled'] == 1 \
219				|| settings['has_wiki_enabled'] == 1 \
220				|| settings['has_devicemanagement_enabled'] == 1 \
221				|| settings['has_calendarserver_enabled'] == 1 \
222				|| settings['has_webdavsharing_enabled'] == 1) ? 1 : 0} \
223			com.apple.message.service_only #{(settings['has_server_webservice_enabled'] == 0 \
224				&& settings['has_wiki_enabled'] == 1 \
225				&& settings['has_devicemanagement_enabled'] == 1 \
226				&& settings['has_calendarserver_enabled'] == 1 \
227				&& settings['has_webdavsharing_enabled'] == 1) ? 1 : 0} \
228			com.apple.message.no_websites_nor_service #{(settings['has_server_webservice_enabled'] == 0 \
229				&& settings['has_wiki_enabled'] == 0 \
230				&& settings['has_devicemanagement_enabled'] == 0 \
231				&& settings['has_calendarserver_enabled'] == 0 \
232				&& settings['has_webdavsharing_enabled'] == 0) ? 1 : 0}`
233	end
234	exec("/usr/sbin/httpd #{ARGV.join(' ')}")
235rescue => e
236	require 'logger'
237	$logger = Logger.new('/var/log/apache2/httpd-wrapper.log')
238	$logger.level = Logger::ERROR
239	$logger.error("Exception raised running httpd-wrapper: #{e.message}")
240	$logger.error("Proceeding with exec of httpd")
241	exec("/usr/sbin/httpd #{ARGV.join(' ')}")
242end
243