Commit d2998245 authored by intrigeri's avatar intrigeri

Import Tails Installer

 - Copied from https://gitlab.tails.boum.org/tails/installer at commit
   71899f2ef3e90cc3d291c49d039eb577e7187bd8, which is the current HEAD of the
   delete-dead-code branch.

 - Integrated into the l10n framework.

refs #17917
parent 8ab9174f
......@@ -57,11 +57,13 @@
/config/chroot_local-includes/usr/share/applications/tails-shutdown.desktop
/config/chroot_local-includes/usr/share/applications/tor-browser.desktop
/config/chroot_local-includes/usr/share/applications/tails-about.desktop
/config/chroot_local-includes/usr/share/applications/tails-installer.desktop
/config/chroot_local-includes/usr/share/applications/unlock-veracrypt-volumes.desktop
/config/chroot_local-includes/usr/share/desktop-directories/Tails.directory
/config/chroot_local-includes/usr/share/polkit-1/actions/org.boum.tails.root-terminal.policy
/config/chroot_local-includes/usr/share/polkit-1/actions/org.boum.tails.additional-software.policy
/config/chroot_local-includes/usr/share/tails/greeter/*.ui
/config/chroot_local-includes/usr/share/tails-installer/*.ui
/config/chroot_local-includes/usr/share/tails/unlock-veracrypt-volumes/*.ui
/tmp/
......
# -*- coding: utf-8 -*-
#
# Copyright © 2008 Red Hat, Inc. All rights reserved.
#
# This copyrighted material is made available to anyone wishing to use, modify,
# copy, or redistribute it subject to the terms and conditions of the GNU
# General Public License v.2. This program is distributed in the hope that it
# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details. You should have
# received a copy of the GNU General Public License along with this program; if
# not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
# Floor, Boston, MA 02110-1301, USA. Any Red Hat trademarks that are
# incorporated in the source code or documentation are not subject to the GNU
# General Public License and may only be used or replicated with the express
# permission of Red Hat, Inc.
#
# Author(s): Luke Macken <lmacken@redhat.com>
import os
import sys
import gettext
import locale
# Add sbin to PATH to support unprivileged mode
if os.path.exists('/usr/sbin') or os.path.exists('/usr/local/sbin'):
try:
os.environ['PATH'] = '/usr/local/sbin:/usr/sbin:' + os.environ['PATH']
except KeyError, e:
os.environ['PATH'] = '/usr/local/sbin:/usr/sbin'
def utf8_gettext(*args, **kwargs):
" Translate string, converting it to a UTF-8 encoded bytestring "
return gettext.translation(
'tails-installer', '/usr/share/locale', fallback=True
).gettext(*args, **kwargs)
_ = utf8_gettext
from tails_installer.creator import TailsInstallerError
from tails_installer.creator import LinuxTailsInstallerCreator as TailsInstallerCreator
from tails_installer.config import config
branding = {
'distribution': config['branding']['distribution'],
'header': config['branding']['header']
}
__all__ = ("TailsInstallerCreator", "TailsInstallerError", "TailsInstallerDialog", "_", "utf8_gettext", "branding")
# -*- coding: utf-8 -*-
config = {
# Minimum device size we accept as valid target for initial
# installation, in MiB as in 1 MiB = 1024**2 bytes. I've seen USB
# sticks labeled "8 GB" that were 7759462400 bytes = 7400 MiB
# large, and one can probably fine even smaller ones, so let's be
# nice with users who believed what was written on the box and
# accept slightly smaller devices than what the theory
# would dictate.
'min_installation_device_size': 7200,
# Minimum device size we tell the user they should get, in MB
# as in 1000 MB = 1 GB, i.e. let's use a unit close to what they will
# see displayed in shops.
'official_min_installation_device_size': 8000,
'main_liveos_dir': 'live',
'running_liveos_mountpoint': '/lib/live/mount/medium',
'liveos_toplevel_files': [ 'autorun.bat', 'autorun.inf', 'boot', '.disk',
'doc', 'EFI', 'live', 'isolinux', 'syslinux',
'tmp', 'utils' ],
'persistence': { 'enabled': False,
},
'branding': { 'distribution': 'Tails',
'header': 'tails-liveusb-header.png',
'color': '#56347c',
'partition_label': 'Tails',
},
}
# -*- coding: utf-8 -*-
import os
import shutil
import sys
import subprocess
from stat import ST_SIZE
from tails_installer import _
from tails_installer.config import config
from tails_installer.utils import (_to_unicode, _dir_size, iso_is_live_system,
unicode_to_utf8, unicode_to_filesystemencoding,
_set_liberal_perms_recursive,
underlying_physical_device, TailsError)
class SourceError(TailsError):
""" A generic error message that is thrown by the Source classes """
pass
class Source(object):
def clone(self, destination):
raise NotImplementedError
class LocalIsoSource(Source):
def __init__(self, path):
self.path = os.path.abspath(_to_unicode(path))
self.size = os.stat(self.path)[ST_SIZE]
if not iso_is_live_system(self.path):
raise SourceError(_("Unable to find LiveOS on ISO"))
self.dev = None
# This can fail for devices not supported by UDisks such as aufs mounts
try:
self.dev = underlying_physical_device(self.path)
except Exception, e:
print >> sys.stderr, _("Could not guess underlying block device: %s"
% e.args[0])
pass
def clone(self, destination):
cmd = ['7z', 'x', self.path,
'-x![BOOT]', '-y', '-o%s' % (destination)]
cmd_decoded = u' '.join(cmd)
cmd_bytes = [ unicode_to_filesystemencoding(el) for el in cmd ]
proc = subprocess.Popen(cmd_bytes, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, err = proc.communicate()
out = out.decode('utf-8')
err = err.decode('utf-8')
if proc.returncode:
raise SourceError(_("There was a problem executing `%s`.\n"
"%s\n%s" % (cmd_decoded, out, err)))
_set_liberal_perms_recursive(destination)
class RunningLiveSystemSource(Source):
def __init__(self, path):
if not os.path.exists(path):
raise SourceError(_("'%s' does not exist") % path)
if not os.path.isdir(path):
raise SourceError(_("'%s' is not a directory") % path)
self.path = path
self.size = _dir_size(self.path)
self.dev = underlying_physical_device(self.path)
def clone(self, destination):
for f in config['liveos_toplevel_files']:
src = os.path.join(self.path, f)
dst = os.path.join(destination, f)
if os.path.isfile(src):
if src.lower().endswith('.iso'):
print >> sys.stderr, _("Skipping '%(filename)s'" % {
'filename': src
})
else:
shutil.copy(src, dst)
elif os.path.islink(src):
linkto = os.readlink(src)
os.symlink(linkto, dst)
elif os.path.isdir(src):
shutil.copytree(src, dst)
_set_liberal_perms_recursive(destination)
# -*- coding: utf-8 -*-
import os
import re
import subprocess
import shutil
import stat
import sys
from tails_installer import _
from tails_installer.config import config
from gi.repository import GLib
class TailsError(Exception):
""" A generic Exception the allows us to manage error
messages encoded in unicode """
def __init__(self, message):
encoded_message=unicode_to_utf8(message)
super(TailsError, self).__init__(encoded_message)
self.message = encoded_message
def __unicode__(self):
return self.message
def _to_unicode(obj, encoding='utf-8'):
if hasattr(obj, 'toUtf8'): # PyQt4.QtCore.QString
obj = str(obj.toUtf8())
if isinstance(obj, basestring):
if not isinstance(obj, unicode):
obj = unicode(obj, encoding)
return obj
def unicode_to_utf8(string):
if isinstance(string, unicode):
return string.encode('utf-8')
return string
def unicode_to_filesystemencoding(string):
if isinstance(string, unicode):
return string.encode(sys.getfilesystemencoding(), 'replace')
return string
def extract_file_content_from_iso(iso_path, path):
""" Return the content of that file read from inside self.iso """
cmd = ['isoinfo', '-R', '-i', unicode_to_utf8(iso_path),
'-x', unicode_to_utf8(path)]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = proc.communicate()
out = unicode_to_utf8(out)
err = unicode_to_utf8(err)
if proc.returncode:
raise Exception(_("There was a problem executing `%s`."
"%s\n%s" % (cmd, out, err)))
return out
def iso_is_live_system(iso_path):
""" Return true iff a Live system is detected inside the iso_path file """
version = extract_file_content_from_iso(iso_path, '/.disk/info')
return version.startswith('Debian GNU/Linux')
def _dir_size(source):
total_size = os.path.getsize(source)
for item in os.listdir(source):
itempath = os.path.join(source, item)
if os.path.isfile(itempath):
total_size += os.path.getsize(itempath)
elif os.path.isdir(itempath):
total_size += _dir_size(itempath)
return total_size
def _move_if_exists(src, dest):
if os.path.exists(src):
shutil.move(src, dest)
def _unlink_if_exists(path):
if os.path.exists(path):
os.unlink(path)
def _set_liberal_perms_recursive(destination):
def _set_liberal_perms(arg, dirname, fnames):
if dirname == 'lost+found':
return
os.chmod(dirname, 0755)
for f in fnames:
if f == 'lost+found':
continue
file = os.path.join(dirname, f)
if os.path.isdir(file):
os.chmod(file, 0755)
elif os.path.isfile(file):
os.chmod(file, 0644)
os.path.walk(destination, _set_liberal_perms, None)
def underlying_physical_device(path):
""" Returns the physical block device UDI on which the specified file is
stored (e.g. /org/freedesktop/UDisks2/block_devices/sdb).
"""
rawdev = os.stat(path)[stat.ST_DEV]
from gi.repository import UDisks
udisksclient = UDisks.Client.new_sync()
block = udisksclient.get_block_for_dev(rawdev)
drive = udisksclient.get_drive_for_block(block)
parentblock = udisksclient.get_block_for_drive(drive, get_physical=False)
return parentblock.get_object_path()
def _format_bytes_in_gb(value):
return '%0.1f GB' % (value / 10.0**9)
def MiB_to_bytes(size_in_MiB):
return size_in_MiB * 1024**2
def _get_datadir():
script_path = os.path.abspath(sys.argv[0])
if not script_path.startswith('/usr/'):
if os.path.exists('data/tails-installer.ui'):
return('data')
elif script_path.startswith('/usr/local/'):
return('/usr/local/share/tails-installer')
else:
return('/usr/share/tails-installer')
def get_open_write_fd(block):
(fd_index, fd_list) = block.call_open_for_restore_sync(
arg_options=GLib.Variant('a{sv}', None)
)
fd = fd_list.get(fd_index.get_handle())
if fd == -1:
raise Exception(_("Could not open device for writing."))
return fd
def write_to_block_device(block, string):
fd = get_open_write_fd(block)
os.write(fd, string)
os.fsync(fd)
os.close(fd)
#!/usr/bin/python -tt
# coding: utf-8
#
# Copyright © 2008-2013 Red Hat, Inc. All rights reserved.
# Copyright © 2012-2016 Tails Developers <tails@boum.org>
#
# This copyrighted material is made available to anyone wishing to use, modify,
# copy, or redistribute it subject to the terms and conditions of the GNU
# General Public License v.2. This program is distributed in the hope that it
# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details. You should have
# received a copy of the GNU General Public License along with this program; if
# not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
# Floor, Boston, MA 02110-1301, USA. Any Red Hat trademarks that are
# incorporated in the source code or documentation are not subject to the GNU
# General Public License and may only be used or replicated with the express
# permission of Red Hat, Inc.
#
# Author(s): Luke Macken <lmacken@redhat.com>
# Tails Developers <tails@boum.org>
import os
import sys
from tails_installer import _
def parse_args():
from optparse import OptionParser
parser = OptionParser()
parser.add_option('-f', '--force', dest='force', action='store',
type='string', help='Force the use of a given drive',
metavar='DRIVE')
parser.add_option('-s', '--safe', dest='safe', action='store_true',
help='Use the "safe, slow and stupid" bootloader')
parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
help='Output extra debugging messages')
parser.add_option('--clone', dest='clone', action='store_true',
help='Clone the currently running Live system')
parser.add_option('-P', '--partition', dest='partition', action='store_true', default=False,
help='Partition the device')
# Let's override argv to effectively ignore the command-line
# arguments until #8861 is solved. We do this (as opposed to
# removing this code) to cheaply ensure we get the same default
# options set.
override_argv = []
# But for our own debugging purposes we still allow these:
if '--verbose' in sys.argv or os.getenv('DEBUG') == '1':
override_argv.append('--verbose')
return parser.parse_args(override_argv)
def main():
opts, args = parse_args()
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from tails_installer.gui import TailsInstallerWindow
try:
win = TailsInstallerWindow(opts=opts, args=sys.argv)
Gtk.main()
except KeyboardInterrupt:
pass
if __name__ == '__main__':
main()
[Desktop Entry]
Type=Application
Name=Tails Installer
Name[de]=Tails Installationsprogramm
Name[fr]=Programme d'installation de Tails
Name[sv]=Tails installerare
Comment=Install, clone, upgrade Tails
Comment[de]=Tails installieren, klonen oder updaten
Comment[fr]=Installer, cloner, mettre à jour Tails
Comment[sv]=Installera, klona, uppgradera Tails
Icon=tails-installer.svg
Exec=/usr/local/bin/tails-installer
Terminal=false
Categories=System;Utility;
Keywords=Tails;Install;Upgrade;Update;Copy;Clone;Installer;USB;Installation;Anonymity;Tor;Anonymous;SD;Amnesic;Amnesia;
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.2 -->
<interface domain="tails-installer">
<requires lib="gtk+" version="3.0"/>
<object class="GtkImage" id="image_next">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">next</property>
</object>
<object class="GtkImage" id="image_question">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">gtk-dialog-question</property>
</object>
<object class="GtkFileFilter" id="isofilter">
<patterns>
<pattern>*.iso</pattern>
<pattern>*.ISO</pattern>
</patterns>
</object>
<object class="GtkListStore" id="liststore_target">
<columns>
<!-- column-name name -->
<column type="gchararray"/>
<!-- column-name id -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkBox" id="box_installer">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkImage" id="image_header">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">baseline</property>
<property name="xalign">0</property>
<property name="pixbuf">tails-liveusb-header.png</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkInfoBar" id="infobar">
<property name="app_paintable">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="show_close_button">True</property>
<signal name="response" handler="on_infobar_response" swapped="no"/>
<child internal-child="action_area">
<object class="GtkButtonBox" id="infobar-action_area">
<property name="can_focus">False</property>
<property name="spacing">6</property>
<property name="layout_style">end</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child internal-child="content_area">
<object class="GtkBox" id="infobar-content_area">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="label_infobar_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="wrap">True</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_infobar_details">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="wrap">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLinkButton" id="help_link">
<property name="label" translatable="yes">Installation Instructions</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">False</property>
<property name="margin_right">18</property>
<property name="xalign">1</property>
<property name="uri">https://tails.boum.org/install/</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box_source">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">18</property>
<property name="margin_right">18</property>
<property name="margin_top">18</property>
<property name="margin_bottom">12</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkBox" id="box_source_select">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkRadioButton" id="radio_button_source_device">
<property name="label" translatable="yes">Clone the current Tails</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">False</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
<property name="group">radio_button_source_iso</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box_source_file">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkRadioButton" id="radio_button_source_iso">
<property name="label" translatable="yes">Use a downloaded Tails ISO image</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">False</property>
<property name="xalign">0</property>