[pkg] remove osx pkg folder

parent cec99a26
Removed all osx packaging scripts for now.
Maintenance now happens in riseup_vpn repo.
Helper files needed for OSX
===========================
* The bitmask-helper that is run as root can be found in the source tree, in
``src/leap/bitmask/vpn/helpers/osx``.
* python ``daemon`` is a dependency for the bitmask-helper, here it is vendored.
* The plist file ``se.leap.bitmask-helper.plist`` (this should be installed into
/Library/LaunchDaemons/se.leap.bitmask-helper.plist).
* OpenVPN up/down scripts: ``openvpn/client.down.sh`` and
``openvpn/client.up.sh``.
#!/bin/sh
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
exec $DIR/bitmask-app
Copying
=======
This work, ‘python-daemon’, is free software: you may copy, modify,
and/or distribute this work under certain conditions; see the relevant
files for specific grant of license. No warranty expressed or implied.
* Parts of this work are licensed to you under the terms of the GNU
General Public License as published by the Free Software Foundation;
version 3 of that license or any later version.
See the file ‘LICENSE.GPL-3’ for details.
* Parts of this work are licensed to you under the terms of the Apache
License, version 2.0 as published by the Apache Software Foundation.
See the file ‘LICENSE.ASF-2’ for details.
author_name = "Ben Finney"
author_email = "ben+python@benfinney.id.au"
year_range = "2001-2017"
copyright = "Copyright © {year_range} {author} and others".format(
year_range=copyright_year_range, author=author)
license = "Apache-2"
url = "https://alioth.debian.org/projects/python-daemon/"
This diff is collapsed.
#!/usr/bin/env bash
sizes=(16 32 64 128 256 512)
largfile='icon_512x512@2x.png'
if [ ! -f "$largfile" ]; then
convert -background none -resize 1024x1024 "$1" "$largfile"
fi
for s in "${sizes[@]}"; do
echo $s
convert -background none -resize ${s}x${s} "$largfile" "icon_${s}x$s.png"
done
cp 'icon_32x32.png' 'icon_16x16@2x.png'
mv 'icon_64x64.png' 'icon_32x32@2x.png'
cp 'icon_256x256.png' 'icon_128x128@2x.png'
cp 'icon_512x512.png' 'icon_256x256@2x.png'
mkdir icon.iconset
mv icon_*x*.png icon.iconset
iconutil -c icns icon.iconset
#!/bin/bash -e
# Note: must be bash; uses bash-specific tricks
#
# ******************************************************************************************************************
# Copyright By Tunnelblick. Redistributed with Bitmask under the GPL.
# This Tunnelblick script does everything! It handles TUN and TAP interfaces,
# pushed configurations and DHCP leases. :)
#
# This is the "Down" version of the script, executed after the connection is
# closed.
#
# Created by: Nick Williams (using original code and parts of old Tblk scripts)
#
# ******************************************************************************************************************
# @param String message - The message to log
logMessage()
{
echo "${@}"
}
# @param String message - The message to log
logDebugMessage()
{
echo "${@}" > /dev/null
}
trim()
{
echo ${@}
}
# @param String list - list of network service names, output from disable_ipv6()
restore_ipv6() {
# Undoes the actions performed by the disable_ipv6() routine in client.up.tunnelblick.sh by restoring the IPv6
# 'automatic' setting for each network service for which that routine disabled IPv6.
#
# $1 must contain the output from disable_ipv6() -- the list of network services.
#
# This routine outputs log messages describing its activities.
if [ "$1" = "" ] ; then
exit
fi
printf %s "$1
" | \
while IFS= read -r ripv6_service ; do
networksetup -setv6automatic "$ripv6_service"
logMessage "Re-enabled IPv6 (automatic) for '$ripv6_service'"
done
}
##########################################################################################
flushDNSCache()
{
if ${ARG_FLUSH_DNS_CACHE} ; then
set +e # "grep" will return error status (1) if no matches are found, so don't fail on individual errors
readonly OSVER="$(sw_vers | grep 'ProductVersion:' | grep -o '10\.[0-9]*')"
set -e # We instruct bash that it CAN again fail on errors
if [ "${OSVER}" = "10.4" ] ; then
if [ -f /usr/sbin/lookupd ] ; then
set +e # we will catch errors from lookupd
/usr/sbin/lookupd -flushcache
if [ $? != 0 ] ; then
logMessage "WARNING: Unable to flush the DNS cache via lookupd"
else
logMessage "Flushed the DNS cache via lookupd"
fi
set -e # bash should again fail on errors
else
logMessage "WARNING: /usr/sbin/lookupd not present. Not flushing the DNS cache"
fi
else
if [ -f /usr/bin/dscacheutil ] ; then
set +e # we will catch errors from dscacheutil
/usr/bin/dscacheutil -flushcache
if [ $? != 0 ] ; then
logMessage "WARNING: Unable to flush the DNS cache via dscacheutil"
else
logMessage "Flushed the DNS cache via dscacheutil"
fi
set -e # bash should again fail on errors
else
logMessage "WARNING: /usr/bin/dscacheutil not present. Not flushing the DNS cache via dscacheutil"
fi
if [ -f /usr/sbin/discoveryutil ] ; then
set +e # we will catch errors from discoveryutil
/usr/sbin/discoveryutil udnsflushcaches
if [ $? != 0 ] ; then
logMessage "WARNING: Unable to flush the DNS cache via discoveryutil udnsflushcaches"
else
logMessage "Flushed the DNS cache via discoveryutil udnsflushcaches"
fi
/usr/sbin/discoveryutil mdnsflushcache
if [ $? != 0 ] ; then
logMessage "WARNING: Unable to flush the DNS cache via discoveryutil mdnsflushcache"
else
logMessage "Flushed the DNS cache via discoveryutil mdnsflushcache"
fi
set -e # bash should again fail on errors
else
logMessage "/usr/sbin/discoveryutil not present. Not flushing the DNS cache via discoveryutil"
fi
set +e # "grep" will return error status (1) if no matches are found, so don't fail on individual errors
hands_off_ps="$( ps -ax | grep HandsOffDaemon | grep -v grep.HandsOffDaemon )"
set -e # We instruct bash that it CAN again fail on errors
if [ "${hands_off_ps}" = "" ] ; then
if [ -f /usr/bin/killall ] ; then
set +e # ignore errors if mDNSResponder isn't currently running
/usr/bin/killall -HUP mDNSResponder
if [ $? != 0 ] ; then
logMessage "mDNSResponder not running. Not notifying it that the DNS cache was flushed"
else
logMessage "Notified mDNSResponder that the DNS cache was flushed"
fi
set -e # bash should again fail on errors
else
logMessage "WARNING: /usr/bin/killall not present. Not notifying mDNSResponder that the DNS cache was flushed"
fi
else
logMessage "WARNING: Hands Off is running. Not notifying mDNSResponder that the DNS cache was flushed"
fi
fi
fi
}
##########################################################################################
resetPrimaryInterface()
{
set +e # "grep" will return error status (1) if no matches are found, so don't fail on individual errors
WIFI_INTERFACE="$(networksetup -listallhardwareports | awk '$3=="Wi-Fi" {getline; print $2}')"
if [ "${WIFI_INTERFACE}" == "" ] ; then
WIFI_INTERFACE="$(networksetup -listallhardwareports | awk '$3=="AirPort" {getline; print $2}')"
fi
PINTERFACE="$( scutil <<-EOF |
open
show State:/Network/Global/IPv4
quit
EOF
grep PrimaryInterface | sed -e 's/.*PrimaryInterface : //'
)"
set -e # resume abort on error
if [ "${PINTERFACE}" != "" ] ; then
if [ "${PINTERFACE}" == "${WIFI_INTERFACE}" -a "${OSVER}" != "10.4" -a -f /usr/sbin/networksetup ] ; then
if [ "${OSVER}" == "10.5" ] ; then
logMessage "Resetting primary interface '${PINTERFACE}' via networksetup -setairportpower off/on..."
/usr/sbin/networksetup -setairportpower off
sleep 2
/usr/sbin/networksetup -setairportpower on
else
logMessage "Resetting primary interface '${PINTERFACE}' via networksetup -setairportpower ${PINTERFACE} off/on..."
/usr/sbin/networksetup -setairportpower "${PINTERFACE}" off
sleep 2
/usr/sbin/networksetup -setairportpower "${PINTERFACE}" on
fi
else
if [ -f /sbin/ifconfig ] ; then
logMessage "Resetting primary interface '${PINTERFACE}' via ifconfig ${PINTERFACE} down/up..."
/sbin/ifconfig "${PINTERFACE}" down
sleep 2
/sbin/ifconfig "${PINTERFACE}" up
else
logMessage "WARNING: Not resetting primary interface because /sbin/ifconfig does not exist."
fi
fi
else
logMessage "WARNING: Not resetting primary interface because it cannot be found."
fi
}
##########################################################################################
trap "" TSTP
trap "" HUP
trap "" INT
export PATH="/bin:/sbin:/usr/sbin:/usr/bin"
readonly OUR_NAME=$(basename "${0}")
logMessage "**********************************************"
logMessage "Start of output from ${OUR_NAME}"
# Remove the flag file that indicates we need to run the down script
if [ -e "/tmp/bitmask-downscript-needs-to-be-run.txt" ] ; then
rm -f "/tmp/bitmask-downscript-needs-to-be-run.txt"
fi
# Test for the "-r" Bitmask option (Reset primary interface after disconnecting) because we _always_ need its value.
# Usually we get the value for that option (and the other options) from State:/Network/OpenVPN,
# but that key may not exist (because, for example, there were no DNS changes).
# So we get the value from the Bitmask options passed to this script by OpenVPN.
#
# We do the same thing for the -f Bitmask option (Flush DNS cache after connecting or disconnecting)
ARG_RESET_PRIMARY_INTERFACE_ON_DISCONNECT="false"
ARG_FLUSH_DNS_CACHE="false"
while [ {$#} ] ; do
if [ "${1:0:1}" != "-" ] ; then # Bitmask arguments start with "-" and come first
break # so if this one doesn't start with "-" we are done processing Bitmask arguments
fi
if [ "$1" = "-r" ] ; then
ARG_RESET_PRIMARY_INTERFACE_ON_DISCONNECT="true"
else
if [ "$1" = "-f" ] ; then
ARG_FLUSH_DNS_CACHE="true"
fi
fi
shift # Shift arguments to examine the next option (if there is one)
done
# Quick check - is the configuration there?
if ! scutil -w State:/Network/OpenVPN &>/dev/null -t 1 ; then
# Configuration isn't there
logMessage "WARNING: Not restoring DNS settings because no saved Bitmask DNS information was found."
flushDNSCache
if ${ARG_RESET_PRIMARY_INTERFACE_ON_DISCONNECT} ; then
resetPrimaryInterface
fi
logMessage "End of output from ${OUR_NAME}"
logMessage "**********************************************"
exit 0
fi
# Get info saved by the up script
TUNNELBLICK_CONFIG="$( scutil <<-EOF
open
show State:/Network/OpenVPN
quit
EOF
)"
ARG_MONITOR_NETWORK_CONFIGURATION="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*MonitorNetwork :' | sed -e 's/^.*: //g')"
LEASEWATCHER_PLIST_PATH="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*LeaseWatcherPlistPath :' | sed -e 's/^.*: //g')"
REMOVE_LEASEWATCHER_PLIST="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*RemoveLeaseWatcherPlist :' | sed -e 's/^.*: //g')"
PSID="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*Service :' | sed -e 's/^.*: //g')"
# Don't need: SCRIPT_LOG_FILE="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*ScriptLogFile :' | sed -e 's/^.*: //g')"
# Don't need: ARG_RESTORE_ON_DNS_RESET="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*RestoreOnDNSReset :' | sed -e 's/^.*: //g')"
# Don't need: ARG_RESTORE_ON_WINS_RESET="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*RestoreOnWINSReset :' | sed -e 's/^.*: //g')"
# Don't need: PROCESS="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*PID :' | sed -e 's/^.*: //g')"
# Don't need: ARG_IGNORE_OPTION_FLAGS="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*IgnoreOptionFlags :' | sed -e 's/^.*: //g')"
ARG_TAP="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*IsTapInterface :' | sed -e 's/^.*: //g')"
ARG_FLUSH_DNS_CACHE="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*FlushDNSCache :' | sed -e 's/^.*: //g')"
ARG_RESET_PRIMARY_INTERFACE_ON_DISCONNECT="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*ResetPrimaryInterface :' | sed -e 's/^.*: //g')"
bRouteGatewayIsDhcp="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*RouteGatewayIsDhcp :' | sed -e 's/^.*: //g')"
bTapDeviceHasBeenSetNone="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*TapDeviceHasBeenSetNone :' | sed -e 's/^.*: //g')"
bAlsoUsingSetupKeys="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*bAlsoUsingSetupKeys :' | sed -e 's/^.*: //g')"
sTunnelDevice="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*TunnelDevice :' | sed -e 's/^.*: //g')"
# Note: '\n' was translated into '\t', so we translate it back (it was done because grep and sed only work with single lines)
sRestoreIpv6Services="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*RestoreIpv6Services :' | sed -e 's/^.*: //g' | tr '\t' '\n')"
# Remove leasewatcher
if ${ARG_MONITOR_NETWORK_CONFIGURATION} ; then
launchctl unload "${LEASEWATCHER_PLIST_PATH}"
if ${REMOVE_LEASEWATCHER_PLIST} ; then
rm -f "${LEASEWATCHER_PLIST_PATH}"
fi
logMessage "Cancelled monitoring of system configuration changes"
fi
if ${ARG_TAP} ; then
if [ "$bRouteGatewayIsDhcp" == "true" ]; then
if [ "$bTapDeviceHasBeenSetNone" == "false" ]; then
if [ -z "$dev" ]; then
# If $dev is not defined, then use TunnelDevice, which was set from $dev by client.up.tunnelblick.sh
# ($def is not defined when this script is called from MenuController to clean up when exiting Bitmask)
if [ -n "${sTunnelDevice}" ]; then
logMessage "WARNING: \$dev not defined; using TunnelDevice: ${sTunnelDevice}"
set +e
ipconfig set "${sTunnelDevice}" NONE 2>/dev/null
set -e
logMessage "Released the DHCP lease via ipconfig set ${sTunnelDevice} NONE."
else
logMessage "WARNING: Cannot configure TAP interface to NONE without \$dev or State:/Network/OpenVPN/TunnelDevice being defined. Device may not have disconnected properly."
fi
else
set +e
ipconfig set "$dev" NONE 2>/dev/null
set -e
logMessage "Released the DHCP lease via ipconfig set $dev NONE."
fi
fi
fi
fi
# Issue warning if the primary service ID has changed
set +e # "grep" will return error status (1) if no matches are found, so don't fail if not found
PSID_CURRENT="$( scutil <<-EOF |
open
show State:/Network/OpenVPN
quit
EOF
grep 'Service : ' | sed -e 's/.*Service : //'
)"
set -e # resume abort on error
if [ "${PSID}" != "${PSID_CURRENT}" ] ; then
logMessage "Ignoring change of Network Primary Service from ${PSID} to ${PSID_CURRENT}"
fi
# Restore configurations
DNS_OLD="$( scutil <<-EOF
open
show State:/Network/OpenVPN/OldDNS
quit
EOF
)"
SMB_OLD="$( scutil <<-EOF
open
show State:/Network/OpenVPN/OldSMB
quit
EOF
)"
DNS_OLD_SETUP="$( scutil <<-EOF
open
show State:/Network/OpenVPN/OldDNSSetup
quit
EOF
)"
TB_NO_SUCH_KEY="<dictionary> {
BitmaskNoSuchKey : true
}"
if [ "${DNS_OLD}" = "${TB_NO_SUCH_KEY}" ] ; then
scutil <<-EOF
open
remove State:/Network/Service/${PSID}/DNS
quit
EOF
else
scutil <<-EOF
open
get State:/Network/OpenVPN/OldDNS
set State:/Network/Service/${PSID}/DNS
quit
EOF
fi
if [ "${DNS_OLD_SETUP}" = "${TB_NO_SUCH_KEY}" ] ; then
if ${bAlsoUsingSetupKeys} ; then
logDebugMessage "DEBUG: Removing 'Setup:' DNS key"
scutil <<-EOF
open
remove Setup:/Network/Service/${PSID}/DNS
quit
EOF
else
logDebugMessage "DEBUG: Not removing 'Setup:' DNS key"
fi
else
if ${bAlsoUsingSetupKeys} ; then
logDebugMessage "DEBUG: Restoring 'Setup:' DNS key"
scutil <<-EOF
open
get State:/Network/OpenVPN/OldDNSSetup
set Setup:/Network/Service/${PSID}/DNS
quit
EOF
else
logDebugMessage "DEBUG: Not restoring 'Setup:' DNS key"
fi
fi
if [ "${SMB_OLD}" = "${TB_NO_SUCH_KEY}" ] ; then
scutil > /dev/null <<-EOF
open
remove State:/Network/Service/${PSID}/SMB
quit
EOF
else
scutil > /dev/null <<-EOF
open
get State:/Network/OpenVPN/OldSMB
set State:/Network/Service/${PSID}/SMB
quit
EOF
fi
logMessage "Restored the DNS and SMB configurations"
set +e # "grep" will return error status (1) if no matches are found, so don't fail if not found
new_resolver_contents="$( grep -v '#' < /etc/resolv.conf )"
set -e # resume abort on error
logDebugMessage "DEBUG:"
logDebugMessage "DEBUG: /etc/resolve = ${new_resolver_contents}"
set +e # scutil --dns will return error status in case dns is already down, so don't fail if no dns found
scutil_dns="$( scutil --dns)"
set -e # resume abort on error
logDebugMessage "DEBUG:"
logDebugMessage "DEBUG: scutil --dns = ${scutil_dns}"
logDebugMessage "DEBUG:"
restore_ipv6 "$sRestoreIpv6Services"
flushDNSCache
# Remove our system configuration data
scutil <<-EOF
open
remove State:/Network/OpenVPN/OldDNS
remove State:/Network/OpenVPN/OldSMB
remove State:/Network/OpenVPN/OldDNSSetup
remove State:/Network/OpenVPN/DNS
remove State:/Network/OpenVPN/SMB
remove State:/Network/OpenVPN
quit
EOF
if ${ARG_RESET_PRIMARY_INTERFACE_ON_DISCONNECT} ; then
resetPrimaryInterface
fi
logMessage "End of output from ${OUR_NAME}"
logMessage "**********************************************"
exit 0
This diff is collapsed.
This diff is collapsed.
#!/bin/sh
# Bitmask Post-Instalation script
# (c) LEAP Encryption access Project
# We copy the bitmask-helper plist to the LaunchDaemons folder, and load the bitmask-helper that runs as root.
LOG=/tmp/bitmask-install.log
chmod +x /Applications/RiseupVPN.app/Contents/Resources/bitmask-helper/bitmask-helper
cp se.leap.bitmask-helper.plist /Library/LaunchDaemons/ \
&& echo `date` ":: Bitmask post-install: copied bitmask-helper Plist." >> $LOG
launchctl load /Library/LaunchDaemons/se.leap.bitmask-helper.plist && echo `date` ":: Bitmask post-install: loaded bitmask-helper." >> $LOG
echo `date` ":: Bitmask post-install: ok." >> $LOG
exit 0
#!/bin/sh
# Bitmask Pre-Instalation script
# (c) LEAP Encryption access Project
# We unload the bitmask-helper if it is running, because we can be installing an upgrade.
LOG=/tmp/bitmask-install.log
ps aux | grep [b]itmask-helper \
&& launchctl unload /Library/LaunchDaemons/se.leap.bitmask-helper.plist \
&& echo `date` ":: Bitmask pre-install: unloaded bitmask-helper." >> $LOG
echo `date` ":: Bitmask pre-install: ok." >> $LOG
exit 0
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>WorkingDirectory</key>
<string>/Applications/RiseupVPN.app/Contents/Resources/bitmask-helper/</string>
<key>StandardOutPath</key>
<string>bitmask-helper.log</string>
<key>StandardErrorPath</key>
<string>bitmask-helper-err.log</string>
<key>GroupName</key>
<string>daemon</string>
<key>RunAtLoad</key>
<true/>
<key>SessionCreate</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>ThrottleInterval</key>
<integer>5</integer>
<key>Label</key>
<string>se.leap.BitmaskHelper</string>
<key>Program</key>
<string>/Applications/RiseupVPN.app/Contents/Resources/bitmask-helper/bitmask-helper</string>
</dict>
</plist>
#!/bin/sh
# Bitmask Post-Instalation script
# (c) LEAP Encryption access Project
# We copy the bitmask-helper plist to the LaunchDaemons folder, and load the bitmask-helper that runs as root.
LOG=/tmp/bitmask-install.log
cp se.leap.bitmask-helper.plist /Library/LaunchDaemons/ \
&& echo `date` ":: Bitmask post-install: copied bitmask-helper Plist." >> $LOG
launchctl load /Library/LaunchDaemons/se.leap.bitmask-helper.plist && echo `date` ":: Bitmask post-install: loaded bitmask-helper." >> $LOG
echo `date` ":: Bitmask post-install: ok." >> $LOG
exit 0
#!/bin/sh
# Bitmask Pre-Instalation script
# (c) LEAP Encryption access Project
# We unload the bitmask-helper if it is running, because we can be installing an upgrade.
LOG=/tmp/bitmask-install.log
ps aux | grep [b]itmask-helper \
&& launchctl unload /Library/LaunchDaemons/se.leap.bitmask-helper.plist \
&& echo `date` ":: Bitmask pre-install: unloaded bitmask-helper." >> $LOG
echo `date` ":: Bitmask pre-install: ok." >> $LOG
exit 0
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>WorkingDirectory</key>
<string>/Applications/Bitmask.app/Contents/Resources/bitmask-helper/</string>
<key>StandardOutPath</key>
<string>bitmask-helper.log</string>
<key>StandardErrorPath</key>
<string>bitmask-helper-err.log</string>
<key>GroupName</key>
<string>daemon</string>
<key>RunAtLoad</key>
<true/>
<key>SessionCreate</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>ThrottleInterval</key>
<integer>5</integer>
<key>Label</key>
<string>se.leap.BitmaskHelper</string>
<key>Program</key>
<string>/Applications/Bitmask.app/Contents/Resources/bitmask-helper/bitmask-helper</string>
</dict>
</plist>
#!/bin/sh
HELPER_PLIST="/Library/LaunchDaemons/se.leap.bitmask-helper.plist"
sudo launchctl unload $HELPER_PLIST
sudo rm -rf /Applications/Bitmask.app
sudo rm -rf ~/Library/Preferences/leap
sudo rm $HELPER_PLIST
echo "Bitmask has been uninstalled from your system!"
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment