apt-offline-1.3.1/0000755000000000000000000000000012157275151010614 5ustar apt-offline-1.3.1/apt-offline-tests.sh0000755000000000000000000001450011547566433014527 0ustar #!/bin/sh DISLIKED_PACKAGES="emacs eclipse gnome" RELEASE="unstable" URI="/tmp/set-$PPID.uris" CACHE_DIR="/var/cache/apt/archives" DOWNLOAD_DIR="/tmp/apt-offline-tests-$PPID" BUNDLE_FILE="/tmp/apt-offline-tests-$PPID.zip" THREADS=5 APT_OFFLINE="./apt-offline --verbose" set_features () { if [ ! -z $1 ]; then URI=$1 fi echo "Executing command 'set $URI'" $APT_OFFLINE set $URI echo "Executing command 'set $URI --simulate '" $APT_OFFLINE set $URI --simulate echo "Executing command 'set $URI --update'" $APT_OFFLINE set $URI --update echo "Executing command 'set $URI --upgrade'" $APT_OFFLINE set $URI --upgrade echo "Executing command 'set $URI --update --upgrade'" $APT_OFFLINE set $URI --update --upgrade echo "Executing command 'set $URI --update --upgrade --upgrade-type upgrade'" $APT_OFFLINE set $URI --update --upgrade --upgrade-type upgrade echo "Executing command 'set $URI --update --upgrade --upgrade-type upgrade --release $RELEASE'" $APT_OFFLINE set $URI --update --upgrade --upgrade-type upgrade --release $RELEASE echo "Executing command 'set $URI --install-packages $DISLIKED_PACKAGES'" $APT_OFFLINE set $URI --install-packages $DISLIKED_PACKAGES echo "Executing command 'set $URI --install-packages $DISLIKED_PACKAGES --release $RELEASE'" $APT_OFFLINE set $URI --install-packages $DISLIKED_PACKAGES --release $RELEASE echo "Executing command 'set $URI --install-src-packages $DISLIKED_PACKAGES'" $APT_OFFLINE set $URI --install-src-packages $DISLIKED_PACKAGES echo "Executing command 'set $URI --install-src-packages $DISLIKED_PACKAGES --release $RELEASE'" $APT_OFFLINE set $URI --install-src-packages $DISLIKED_PACKAGES --release $RELEASE echo "Executing command 'set $URI --src-build-dep --install-src-packages $DISLIKED_PACKAGES'" $APT_OFFLINE set $URI --src-build-dep --install-src-packages $DISLIKED_PACKAGES echo "Executing command 'set $URI --src-build-dep --install-src-packages $DISLIKED_PACKAGES --release $RELEASE'" $APT_OFFLINE set $URI --src-build-dep --install-src-packages $DISLIKED_PACKAGES --release $RELEASE } get_features () { if [ ! -z $1 ]; then URI=$1 fi echo "Executing command 'get $URI '" $APT_OFFLINE get $URI echo "Executing command 'get $URI --threads $THREADS'" $APT_OFFLINE get $URI --threads $THREADS echo "Executing command 'get $URI --threads $THREADS --socket-timeout 30'" $APT_OFFLINE get $URI --threads $THREADS --socket-timeout 30 echo "Executing command 'get $URI --threads $THREADS -d $DOWNLOAD_DIR'" $APT_OFFLINE get $URI --threads $THREADS -d $DOWNLOAD_DIR echo "Executing command 'get $URI --threads $THREADS -d $DOWNLOAD_DIR --cache-dir $CACHE_DIR'" $APT_OFFLINE get $URI --threads $THREADS -d $DOWNLOAD_DIR --cache-dir $CACHE_DIR echo "Executing command 'get $URI --no-checksum -d $DOWNLOAD_DIR --cache-dir $CACHE_DIR'" $APT_OFFLINE get $URI --no-checksum -d $DOWNLOAD_DIR --cache-dir $CACHE_DIR echo "Executing command 'get $URI --bug-reports --threads $THREADS -d $DOWNLOAD_DIR --cache-dir $CACHE_DIR'" $APT_OFFLINE get $URI --threads $THREADS --bug-reports -d $DOWNLOAD_DIR --cache-dir $CACHE_DIR echo "Executing command 'get $URI --bug-reports --threads $THREADS --bundle $BUNDLE_FILE -d $DOWNLOAD_DIR --cache-dir $CACHE_DIR'" $APT_OFFLINE get $URI --threads $THREADS --bug-reports -d $DOWNLOAD_DIR --cache-dir $CACHE_DIR --bundle $BUNDLE_FILE } install_features () { if [ ! -z $1 ]; then DOWNLOAD_DIR=$1 BUNDLE_FILE=$1 fi echo "Executing command 'install $DOWNLOAD_DIR --skip-bug-reports'" $APT_OFFLINE install $DOWNLOAD_DIR --skip-bug-reports echo "Executing command 'install $DOWNLOAD_DIR --simulate --skip-bug-reports'" $APT_OFFLINE install $DOWNLOAD_DIR --simulate --skip-bug-reports echo "Executing command 'install $DOWNLOAD_DIR --skip-bug-reports'" $APT_OFFLINE install $DOWNLOAD_DIR --simulate --skip-bug-reports echo "Executing command 'install $DOWNLOAD_DIR --skip-bug-reports --allow-unauthenticated'" $APT_OFFLINE install $DOWNLOAD_DIR --simulate --skip-bug-reports --allow-unauthenticated echo "Executing command 'install $BUNDLE_FILE --skip-bug-reports'" $APT_OFFLINE install $BUNDLE_FILE --skip-bug-reports echo "Executing command 'install $BUNDLE_FILE --simulate --skip-bug-reports'" $APT_OFFLINE install $BUNDLE_FILE --simulate --skip-bug-reports echo "Executing command 'install $BUNDLE_FILE --skip-bug-reports'" $APT_OFFLINE install $BUNDLE_FILE --simulate --skip-bug-reports echo "Executing command 'install $BUNDLE_FILE --skip-bug-reports --allow-unauthenticated'" $APT_OFFLINE install $BUNDLE_FILE --simulate --skip-bug-reports --allow-unauthenticated } install_features_prompt () { if [ ! -z $1 ]; then DOWNLOAD_DIR=$1 BUNDLE_FILE=$1 fi echo "Executing command 'install $DOWNLOAD_DIR '" $APT_OFFLINE install $DOWNLOAD_DIR echo "Executing command 'install $DOWNLOAD_DIR --simulate'" $APT_OFFLINE install $DOWNLOAD_DIR --simulate echo "Executing command 'install $DOWNLOAD_DIR'" $APT_OFFLINE install $DOWNLOAD_DIR --simulate echo "Executing command 'install $DOWNLOAD_DIR --allow-unauthenticated'" $APT_OFFLINE install $DOWNLOAD_DIR --simulate --allow-unauthenticated echo "Executing command 'install $BUNDLE_FILE '" $APT_OFFLINE install $BUNDLE_FILE echo "Executing command 'install $BUNDLE_FILE --simulate'" $APT_OFFLINE install $BUNDLE_FILE --simulate echo "Executing command 'install $BUNDLE_FILE'" $APT_OFFLINE install $BUNDLE_FILE --simulate echo "Executing command 'install $BUNDLE_FILE --allow-unauthenticated'" $APT_OFFLINE install $BUNDLE_FILE --simulate --allow-unauthenticated } all_features () { echo "Executing function set_features" set_features echo "Executing function get_features" get_features echo "Executing function install_features" install_features } case $1 in "set") if [ ! -z $2 ]; then set_features $2 else set_features fi ;; "get") if [ ! -z $2 ]; then get_features $2 else get_features fi ;; "install_features_promptless") if [ ! -z $2 ]; then install_features $2 else install_features fi ;; "install") # With prompts for bug reports if [ ! -z $2 ]; then install_features_prompt $2 else install_features_prompt fi ;; "--help") echo "$0 [set || get || install_features_promptless || install]" exit 0; ;; "-h") echo "$0 [set || get || install_features_promptless || install]" exit 0; ;; *) all_features ;; esac apt-offline-1.3.1/apt-offline.80000644000000000000000000001523411745777322013127 0ustar .TH apt-offline 8 "April, 2011" "version 1.1" "USER COMMANDS" .SH NAME apt-offline \- Offline APT Package manager .SH SYNOPSIS .B apt-offline [ARGUMENTS] [OPTIONS] .SH DESCRIPTION apt-offline brings offline package management functionality to Debian based system. It can be used to download packages and its dependencies to be installed later on (or required to update) a disconnected machine. Packages can be downloaded from a different connected machine. .PP It can also fetch bug reports for the packages that are downloaded. .PP Unless the \-h/\-v or \-\-help/\-\-version options are given, one of the .B get, set or .B install commands must be provided. .SS set FILENAME [-h] [--install-packages PKG...PKG_N] [--install-src-packages PKG...PKG_N] --src-build-dep [--release release_name] [--update] [--upgrade] [--upgrade-type upgrade] .PP .B set generates a signature file with the data required to install on or update the disconnected machine. .B FILENAME {apt-offline.sig} is the signature file generated on the machine. This file will contain all the information necessary for apt. .IP "\fB\-\-install\-packages [PKG...PKG_N]\fP" 10 Packages that need to be installed .IP "\fB\-\-install\-src\-packages [PKG...PKG_N]\fP" 10 Source packages that need to be installed .IP "\fB\-\-src\-build\-dep\fP" 10 Download Build Dependency packages for the source packages requested .IP "\fB\-\-release release_name\fP" 10 Release target to install packages from .IP "\fB\-\-update\fP" 10 Generate APT Database signature for an update. This is the equivalent of using .B "apt-get update" .IP "\fB\-\-upgrade\fP" 10 Generate APT Database signature for package upgrade. This is the equivalent of using .B "apt-get upgrade" .IP "\fB\-\-upgrade\-type {upgrade_type}\fP" 10 Type of upgrade you would like to perform. Default is .B "upgrade". Other valid types are .B "dist-upgrade" and "dselect-upgrade" .SS get FILENAME [-h] [--socket-timeout ] [-d / --download-dir] [-s / --cache-dir] [--no-checksum] [-t / --threads ] [--bundle] [--bug-reports] [--proxy-host] [--proxy-port] .PP .B get downloads APT data as per a signature file. .B FILENAME {apt-offline.sig} is the signature file required for details about data to be downloaded. This file should have been generated by the above .B set command. .IP "\fB\-d, \-\-download\-dir DIR_NAME\fP" 10 Download data to the specified DIR_NAME folder. If no folder name is specified, data is downloaded to a folder in the TEMPDIR path in the format .B apt-offline-download-$PID .IP "\fB\-s, \-\-cache\-dir DIR_NAME\fP" 10 Look for data in the cache before downloading it from the internet. If you are on a Debian box, you would want to specify .I /var/cache/apt/archives here. If the data is not available in the cache, the downloaded data is also copied to the cache. .IP "\fB\-\-no\-checksum\fP" 10 Enabling this option will bypass the checksum verification of each downloaded file thus losing integrity of the package. Usage of this option is highly discouraged .IP "\fB\-t, \-\-threads NUM_OF_THREADS\fP" 10 Number of threads to spawn for downloads. Default is 1. Using too many threads can overload the servers, hence it is advisable to keep the number low .IP "\fB\-\-bundle FILENAME\fP" 10 Create an archive file FILENAME. The file is archived in zip format .IP "\fB\-\-bug\-reports\fP" 10 Download bug reports for packages that are being downloaded. Currently only the Debian BTS is supported. .IP "\fB\-\-proxy\-host\fP" 10 Specify the Proxy Host to be used. .IP "\fB\-\-proxy\-port\fP" 10 Specify the Proxy port number to be used. .SS install FILE/FOLDER [-h] [--skip-bug-reports ] [--install-src-path PATH] [--allow-unauthenticated] .PP .B install installs APT data to the APT package database and updates it. .B FILE {archive_bundle_file} Install data from the archive (bundle) file. .B FOLDER Install data from the folder path. Either FILE or FOLDER argument can be provided to the .B install command. .IP "\fB\-\-skip\-bug\-reports\fP" 10 Skip listing of downloaded bug reports, if any. .IP "\fB\-\-allow\-unauthenticated\fP" 10 Don't verify GPG signatures for the data to be installed to APT. Usage of this option is highly discouraged. .IP "\fB\-\-install\-src\-path PATH\fP" 10 Path to filesystem where we want the source packages to be installed to. Default will be a folder in your TEMPDIR. .SH GLOBAL OPTIONS .TP \-h, \-\-help Show help message .TP \-\-verbose Run in verbose mode .TP \-\-test\-windows Developer only. Used for testing on windows .TP \-v, \-\-version Display the version of the program .SH EXAMPLES .TP .B To keep your disconnected machine up-to-date, here is a typical workflow .TP .B NOTE: argument/option handling apt\-offline relies on argparse for argument/option parsing. To explicitly instruct apt\-offline about an argument, you can pass it with the \-\- delimiter. .B Ex. apt\-offline set \-\-update \-\-upgrade \-\-install\-packages wm2 \-\- foo.sig By specifying the .B \-\- delimiter, we instruct apt\-offline that foo.sig is an argument to the .B apt\-offline command and not to the .B \-\-install\-packages option. .TP .B apt-offline set FILENAME This command will generate a signature file FILENAME for APT Package Database. To generate only the signature for updates, use the \-\-update option. To generate only the signature for package upgrades, use the \-\-upgrade option. Default behavior when no options are specified is to generate a signature for both the operations. .TP .B apt-offline get FILENAME This command will fetch the data required for APT Package Database as per the signature file FILENAME generated by .B apt-offline get. To download bug reports also use the \-\-bug\-reports option. Currently supported bug tracker is Debian BTS only. By default, if neither of \-d or \-\-bundle options are specified, apt-offline downloads data into a folder inside the TEMPDIR environment folder in the format apt\-offline\-downloads\-PID, where PID is the PID of the running apt\-offline process. Example on a linux machine would be something like: /tmp/apt-offline-downloads-23242/ .TP .B apt-offline install FILE|FOLDER This command will sync the data downloaded by .B apt-offline get to the APT Package Database and update it. Depending on where the data was downloaded to or packed into, either the absolute FOLDER path or the archive FILE path can be specified. .SH AUTHOR .B apt-offline is written by Ritesh Raj Sarraf (rrs@researchut.com) If you wish to report a bug in apt-offline, please see .B http://apt-offline.alioth.debian.org or send an email to me at .B rrs@researchut.com .SH SEE ALSO apt-get(8), apt-cache(8), dpkg(8), aptitude(8), .SH DEDICATION This software is dedicated to the memory of my father Santosh Kumar Sarraf. We miss you a lot. apt-offline-1.3.1/apt_offline_core/0000755000000000000000000000000012157275265014120 5ustar apt-offline-1.3.1/apt_offline_core/AptOffline_urlutils.py0000644000000000000000000001353111350136350020450 0ustar # # urlutils.py - Simplified urllib handling # # Written by Chris Lawrence # (C) 1999-2006 Chris Lawrence # # This program is freely distributable per the following license: # ## Permission to use, copy, modify, and distribute this software and its ## documentation for any purpose and without fee is hereby granted, ## provided that the above copyright notice appears in all copies and that ## both that copyright notice and this permission notice appear in ## supporting documentation. ## ## I DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL ## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL I ## BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY ## DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, ## WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ## ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS ## SOFTWARE. # # Version 3.35; see changelog for revision history import httplib import urllib import urllib2 import getpass import re import socket import commands import os import sys from AptOffline_reportbug_exceptions import * try: import webbrowser except: webbrowser = None UA_STR = 'reportbug/3.35 (Debian)' def decode (page): "gunzip or deflate a compressed page" #print page.info().headers encoding = page.info().get("Content-Encoding") if encoding in ('gzip', 'x-gzip', 'deflate'): from cStringIO import StringIO # cannot seek in socket descriptors, so must get content now content = page.read() if encoding == 'deflate': import zlib fp = StringIO(zlib.decompress(content)) else: import gzip fp = gzip.GzipFile('', 'rb', 9, StringIO(content)) # remove content-encoding header headers = httplib.HTTPMessage(StringIO("")) ceheader = re.compile(r"(?i)content-encoding:") for h in page.info().keys(): if not ceheader.match(h): headers[h] = page.info()[h] newpage = urllib.addinfourl(fp, headers, page.geturl()) # Propagate code, msg through if hasattr(page, 'code'): newpage.code = page.code if hasattr(page, 'msg'): newpage.msg = page.msg return newpage return page class HttpWithGzipHandler (urllib2.HTTPHandler): "support gzip encoding" def http_open (self, req): return decode(urllib2.HTTPHandler.http_open(self, req)) if hasattr(httplib, 'HTTPS'): class HttpsWithGzipHandler (urllib2.HTTPSHandler): "support gzip encoding" def http_open (self, req): return decode(urllib2.HTTPSHandler.http_open(self, req)) class handlepasswd(urllib2.HTTPPasswordMgrWithDefaultRealm): def find_user_password(self, realm, authurl): user, password = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(self, realm, authurl) if user is not None: return user, password user = raw_input('Enter username for %s at %s: ' % (realm, authurl)) password = getpass.getpass( "Enter password for %s in %s at %s: " % (user, realm, authurl)) self.add_password(realm, authurl, user, password) return user, password _opener = None def urlopen(url, proxies=None, data=None): global _opener if not proxies: proxies = urllib.getproxies() headers = {'User-Agent': UA_STR, 'Accept-Encoding' : 'gzip;q=1.0, deflate;q=0.9, identity;q=0.5'} req = urllib2.Request(url, data, headers) proxy_support = urllib2.ProxyHandler(proxies) if _opener is None: pwd_manager = handlepasswd() handlers = [proxy_support, urllib2.UnknownHandler, HttpWithGzipHandler, urllib2.HTTPBasicAuthHandler(pwd_manager), urllib2.ProxyBasicAuthHandler(pwd_manager), urllib2.HTTPDigestAuthHandler(pwd_manager), urllib2.ProxyDigestAuthHandler(pwd_manager), urllib2.HTTPDefaultErrorHandler, urllib2.HTTPRedirectHandler, ] if hasattr(httplib, 'HTTPS'): handlers.append(HttpsWithGzipHandler) _opener = urllib2.build_opener(*handlers) # print _opener.handlers urllib2.install_opener(_opener) return _opener.open(req) # Global useful URL opener; returns None if the page is absent, otherwise # like urlopen def open_url(url, http_proxy=None): proxies = urllib.getproxies() if http_proxy: proxies['http'] = http_proxy try: page = urlopen(url, proxies) except urllib2.HTTPError, x: if x.code in (404, 500, 503): return None else: raise except (socket.gaierror, socket.error, urllib2.URLError), x: raise NoNetwork except IOError, data: if data and data[0] == 'http error' and data[1] == 404: return None else: raise NoNetwork except TypeError: print >> sys.stderr, "http_proxy environment variable must be formatted as a valid URI" raise NoNetwork return page def launch_browser(url): if not os.system('command -v sensible-browser &> /dev/null'): cmd = 'sensible-browser' + commands.mkarg(url) os.system(cmd) return if webbrowser: webbrowser.open(url) return X11BROWSER = os.environ.get('X11BROWSER', 'mozilla-firefox') CONSOLEBROWSER = os.environ.get('CONSOLEBROWSER', 'lynx') if (os.environ.has_key('DISPLAY') and not os.system('command -v '+X11BROWSER+' &> /dev/null')): cmd = "%s %s &" % (X11BROWSER, commands.mkarg(url)) else: cmd = "%s %s" % (CONSOLEBROWSER, commands.mkarg(url)) os.system(cmd) if __name__ == '__main__': page = open_url('http://bugs.debian.org/reportbug') content = page.read() print page.info().headers apt-offline-1.3.1/apt_offline_core/AptOfflineLib.py0000644000000000000000000006343211547566433017162 0ustar ############################################################################ # Copyright (C) 2005, 2009 Ritesh Raj Sarraf # # rrs@researchut.com # # # # This program is free software; you can redistribute it and#or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty 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., # # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################ import os import sys import threading import Queue import zipfile import bz2 import gzip # Test Comment # For Forest WindowColor = True try: import WConio except ImportError: WindowColor = False from array import array import signal #INFO: They aren't on Windows try: from fcntl import ioctl import termios except ImportError: pass #INFO: Python 2.5 introduces hashlib. # This module supports many hash/digest algorithms # We do this check till Python 2.5 becomes widely used. Python_2_5 = True try: import hashlib except ImportError: Python_2_5 = False class Checksum: def HashMessageDigestAlgorithms( self, checksum, HashType, file ): data = open( file, 'rb' ) if HashType == "sha256": Hash = self.sha256( data ) elif HashType == "md5" or HashType == "md5sum": Hash = self.md5( data ) else: Hash = None data.close() if Hash == checksum: return True return False def sha256( self, data ): hash = hashlib.sha256() hash.update( data.read() ) return hash.hexdigest() def md5( self, data ): hash = hashlib.md5() hash.update( data.read() ) return hash.hexdigest() def CheckHashDigest( self, file, checksum ): type = checksum.split(":")[0] type = type.lower() checksum = checksum.split( ":" )[1] return self.HashMessageDigestAlgorithms( checksum, type, file ) class Log: '''A OOP implementation for logging. warnings is to tackle the warning option verbose is to tackle the verbose option color is if you want to colorize your output You should pass these options, taking it from optparse/getopt, during instantiation''' # WConio can provide simple coloring mechanism for Microsoft Windows console # Color Codes: # Black = 0 # Green = 2 # Red = 4 # White = 15 # Light Red = 12 # Light Cyan = 11 # # #FIXME: The Windows Command Interpreter does support colors natively. I think that support has been since Win2k. # That's all for Windows Command Interpreter. # # As for ANSI Compliant Terminals (which most Linux/Unix Terminals are.)..... # I think the ANSI Color Codes would be good enough for my requirements to print colored text on an ANSI compliant terminal. # # The ANSI Terminal Specification gives programs the ability to change the text color or background color. # An ansi code begins with the ESC character [^ (ascii 27) followed by a number (or 2 or more separated by a semicolon) and a letter. # # In the case of colour codes, the trailing letter is "m"... # # So as an example, we have ESC[31m ... this will change the foreground colour to red. # # The codes are as follows: # # For Foreground Colors # 1m - Hicolour (bold) mode # 4m - Underline (doesn't seem to work) # 5m - BLINK!! # 8m - Hidden (same colour as bg) # 30m - Black # 31m - Red # 32m - Green # 33m - Yellow # 34m - Blue # 35m - Magenta # 36m - Cyan # 37m - White # # For Background Colors # # 40m - Change Background to Black # 41m - Red # 42m - Green # 43m - Yellow # 44m - Blue # 45m - Magenta # 46m - Cyan # 47m - White # # 7m - Change to Black text on a White bg # 0m - Turn off all attributes. # # Now for example, say I wanted blinking, yellow text on a magenta background... I'd type ESC[45;33;5m def __init__( self, verbose, lock=None ): self.VERBOSE = bool( verbose ) self.color_syntax = '\033[1;' if lock is True: self.DispLock = threading.Lock() self.lock = True else: self.DispLock = False self.lock = False if os.name == 'posix': self.platform = 'posix' self.color = {'Red': '31m', 'Black': '30m', 'Green': '32m', 'Yellow': '33m', 'Blue': '34m', 'Magneta': '35m', 'Cyan': '36m', 'White': '37m', 'Bold_Text': '1m', 'Underline': '4m', 'Blink': '5m', 'SwitchOffAttributes': '0m'} elif os.name in ['nt', 'dos']: self.platform = None if WindowColor is True: self.platform = 'microsoft' self.color = {'Red': 4, 'Black': 0, 'Green': 2, 'White': 15, 'Cyan': 11, 'SwitchOffAttributes': 15} else: self.platform = None self.color = None def set_color( self, color ): '''Check the platform and set the color''' if self.platform == 'posix': sys.stdout.write( self.color_syntax + self.color[color] ) sys.stderr.write( self.color_syntax + self.color[color] ) elif self.platform == 'microsoft': WConio.textcolor( self.color[color] ) def msg( self, msg ): '''Print general messages. If locking is available use them.''' if self.lock: self.DispLock.acquire( True ) #self.set_color( 'White' ) sys.stdout.write( msg ) sys.stdout.flush() #self.set_color( 'SwitchOffAttributes' ) if self.lock: self.DispLock.release() def err( self, msg ): '''Print messages with an error. If locking is available use them.''' if self.lock: self.DispLock.acquire( True ) self.set_color( 'Red' ) sys.stderr.write( "ERROR: " + msg ) sys.stderr.flush() self.set_color( 'SwitchOffAttributes' ) if self.lock: self.DispLock.release() def success( self, msg ): '''Print messages with a success. If locking is available use them.''' if self.lock: self.DispLock.acquire( True ) self.set_color( 'Green' ) sys.stdout.write( msg ) sys.stdout.flush() self.set_color( 'SwitchOffAttributes' ) if self.lock: self.DispLock.release() # For the rest, we need to check the options also def verbose( self, msg ): '''Print verbose messages. If locking is available use them.''' if self.lock: self.DispLock.acquire( True ) if self.VERBOSE is True: self.set_color( 'Cyan' ) sys.stdout.write( "VERBOSE: " + msg ) sys.stdout.flush() self.set_color( 'SwitchOffAttributes' ) if self.lock: self.DispLock.release() def calcSize( self, size ): ''' Takes number of kB and returns a string of proper size. Like if > 1024, return a megabyte ''' if size > 1024: size = size // 1024 if size > 1024: size = size // 1024 return ( "%d GiB" % ( size ) ) return ( "%d MiB" % ( size ) ) return ( "%d KiB" % ( size ) ) class ProgressBar( object ): def __init__( self, minValue=0, maxValue=0, width=None, total_items=None, fd=sys.stderr ): #width does NOT include the two places for [] markers self.min = minValue self.max = maxValue self.span = float( self.max - self.min ) self.fd = fd self.signal_set = False if width is None: try: self.handle_resize( None, None ) signal.signal( signal.SIGWINCH, self.handle_resize ) self.signal_set = True except: self.width = 79 #The standard else: self.width = width self.value = self.min if total_items is None or total_items <= 0: self.items = 0 #count of items being tracked self.items_update = True else: self.items = total_items self.items_update = False self.complete = 0 def handle_resize( self, signum, frame ): h, w = array( 'h', ioctl( self.fd, termios.TIOCGWINSZ, '\0' * 8 ) )[:2] self.width = w def updateValue( self, newValue ): #require caller to supply a value! newValue is the increment from last call self.value = max( self.min, min( self.max, self.value + newValue ) ) self.display() def completed( self ): self.complete = self.complete + 1 if self.signal_set: signal.signal( signal.SIGWINCH, signal.SIG_DFL ) self.display() def addItem( self, maxValue ): self.max = self.max + maxValue self.span = float( self.max - self.min ) if self.items_update is True: self.items = self.items + 1 self.display() def display( self ): print "\r%3s /%3s items: %s\r" % ( self.complete, self.items, str( self ) ), def __str__( self ): #compute display fraction percentFilled = ( ( self.value - self.min ) / self.span ) widthFilled = int( self.width * percentFilled + 0.5 ) return ( "[" + "#"*widthFilled + " " * ( self.width - widthFilled ) + "]" + " %5.1f%% of %s" % ( percentFilled * 100.0, self.__numStr__( self.max / 1024 ) ) ) def __numStr__( self, size ): if size > 1024: size = size / 1024 if size > 1024: size = size / 1024 return ( "%d GiB" % ( size ) ) return ( "%d MiB" % ( size ) ) return ( "%d KiB" % ( size ) ) class Archiver: def __init__( self, lock=None ): if lock is None or lock != 1: self.ZipLock = False else: self.ZipLock = threading.Lock() self.lock = True def TarGzipBZ2_Uncompress( self, SourceFileHandle, TargetFileHandle ): try: TargetFileHandle.write( SourceFileHandle.read() ) except EOFError: pass except IOError: #TODO: What constitutes an "IOError: invalid data stream" ??? # Couldn't find much from the docs. Needs to be investigated. # Answer: # A BZ2 file corruption is seen during file creation only. # Perhaps it has to do with the bad netowrk, loss of packets et cetera # The safest bet at the moment is to simply discard such files, which were # downloaded in damaged form. return False return True def compress_the_file( self, zip_file_name, files_to_compress ): '''Condenses all the files into one single file for easy transfer''' try: if self.lock: self.ZipLock.acquire( True ) filename = zipfile.ZipFile( zip_file_name, "a" ) fileOpened = True except IOError: #INFO: By design zipfile throws an IOError exception when you open # in "append" mode and the file is not present. fileOpened = False try: filename = zipfile.ZipFile( zip_file_name, "w" ) fileOpened = True except IOError: fileOpened = False if fileOpened: #Supported from Python 2.5 ?? filename.write( files_to_compress, os.path.basename( files_to_compress ), zipfile.ZIP_DEFLATED ) filename.close() if self.lock: self.ZipLock.release() return True else: return False def decompress_the_file( self, archive_file, target_file, archive_type ): '''Extracts all the files from a single condensed archive file''' if archive_type == "bzip2" or archive_type == "gzip": if archive_type == "bzip2": try: read_from = bz2.BZ2File( archive_file, 'r' ) except IOError: return False elif archive_type == "gzip": try: read_from = gzip.GzipFile( archive_file, 'r' ) except IOError: return False else: return False try: write_to = open ( target_file, 'wb' ) except IOError: return False if self.TarGzipBZ2_Uncompress( read_from, write_to ) != True: #INFO: Return False for the stream that failed. return False write_to.close() read_from.close() return True elif archive_type == "zip": #INFO: We will never reach here. # Package data from Debian is usually served only in bz2 or gzip format # Plain zip is something we might never see. # Leaving it here just like that. Maybe we will use it in the future # FIXME: This looks odd. Where are we writing to a file ??? try: zip_file = zipfile.ZipFile( archive_file, 'r' ) except IOError: return False #FIXME: for filename in zip_file.namelist(): try: write_to = open ( filename, 'wb' ) except IOError: return False write_to.write(zip_file.read(filename) ) write_to.flush() write_to.close() zip_file.close() return True else: return False class FileMgmt( object ): def __init__( self ): self.duplicate_files = [] def files( self, root ): for path, folders, files in os.walk( root ): for file in files: yield path, file def find_first_match( self, cache_dir=None, filename=None ): '''Return the full path of the filename if a match is found Else Return False''' if cache_dir is None: return False elif filename is None: return False elif os.path.isdir( cache_dir ) is False: return False else: for path, file in self.files( cache_dir ): if file == filename: return os.path.join( path, file ) return False def rename_file( self, orig, new ): '''Rename file from orig to new''' if not os.path.isfile( orig ): return False os.rename( orig, new ) return True def remove_file( self, src ): '''Remvoe the given src file.''' try: os.unlink( src ) except IOError: return False def move_file( self, src, dest ): '''Move file from src to dest.''' if not os.path.isdir( dest ): return False try: os.rename( src, dest + "/" + os.path.basename( src ) ) except IOError: return False def move_folder( self, src, dest ): '''Move folder from src to dest.''' if os.path.isdir( dest ): try: os.rename( src, dest + "/" + os.path.basename( src ) ) except IOError: return False def find_dup( self, dir ): '''"dir" will be the directory withing which duplicate files are searched Returns a list with the duplicates''' #TODO: This is buggy currently for xpath, yfile in dir: for path, file in dir: if file == yfile: if not ( xpath + "/" + yfile == path + "/" + file ): if [xpath + "/" + yfile, path + "/" + file] in self.duplicate_files: break else: self.duplicate_files += [ [xpath + "/" + yfile, path + "/" + file] ] #self.duplicate_files = set(self.duplicate_files) len = self.duplicate_files.__len__() print len for x in range( len ): self.duplicate_files[x].sort() self.duplicate_files.sort() num = 0 number = 0 for ( x, y ) in self.duplicate_files: while number < len - 1: if x in self.duplicate_files[number] or y in self.duplicate_files[number]: num += 1 print num if num > 1: print "Num went 2" self.duplicate_files.pop( number ) num -= 0 number += 1 return self.duplicate_files class MyThread( threading.Thread ): """My thread class""" def __init__( self, WorkerFunction, requestQueue=None, responseQueue=None, NumOfThreads=1 ): # Pool of NUMTHREADS Threads that run run(). self.requestQueue = requestQueue self.responseQueue = responseQueue self.threads = NumOfThreads self.threads_finished = 0 # used by gui to understand if things got over self.guiTerminateSignal=False self.WorkerFunction = WorkerFunction self.thread_pool = [ threading.Thread( target=self.run, args=() ) for i in range( self.threads ) ] for thread in self.thread_pool: thread.guiTerminateSignal=False def startThreads( self ): for thread in self.thread_pool: thread.start() def stopThreads( self ): '''Shut down the threads after all requests end. (Put one None "sentinel" for each thread.)''' for thread in self.thread_pool: self.requestQueue.put( None ) def populateQueue( self, item ): self.requestQueue.put( item ) def stopQueue( self, timeout=0 ): '''Don't end the program prematurely. (Note that because Queue.get() is blocking by defualt this isn't strictly necessary. But if you were, say, handling responses in another thread, you'd want something like this in your main thread.)''' if timeout !=0: self.threads_finished = 0 # recount finished threads if gui handler needs for thread in self.thread_pool: if timeout==0: thread.join() else: # let threads also lookout for gui signals of cancellation thread.join(timeout) if not thread.isAlive(): self.threads_finished += 1 def run( self, item=None): while True: if threading.currentThread().guiTerminateSignal: #print threading.currentThread().getName(), "has been stopped :D" break if self.requestQueue is not None: item = self.requestQueue.get() if item is None: break thread_name = threading.currentThread().getName() if self.responseQueue is not None: self.responseQueue.put( self.WorkerFunction( item, thread_name ) ) exit_status = self.responseQueue.get() else: self.WorkerFunction( item, thread_name ) apt-offline-1.3.1/apt_offline_core/AptOffline_argparse.py0000644000000000000000000025247011465541222020405 0ustar # -*- coding: utf-8 -*- # Copyright © 2006-2009 Steven J. Bethard . # # Licensed under the Apache License, Version 2.0 (the "License"); you may not # use this file except in compliance with the License. You may obtain a copy # of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Command-line parsing library This module is an optparse-inspired command-line parsing library that: - handles both optional and positional arguments - produces highly informative usage messages - supports parsers that dispatch to sub-parsers The following is a simple usage example that sums integers from the command-line and writes the result to a file:: parser = argparse.ArgumentParser( description='sum the integers at the command line') parser.add_argument( 'integers', metavar='int', nargs='+', type=int, help='an integer to be summed') parser.add_argument( '--log', default=sys.stdout, type=argparse.FileType('w'), help='the file where the sum should be written') args = parser.parse_args() args.log.write('%s' % sum(args.integers)) args.log.close() The module contains the following public classes: - ArgumentParser -- The main entry point for command-line parsing. As the example above shows, the add_argument() method is used to populate the parser with actions for optional and positional arguments. Then the parse_args() method is invoked to convert the args at the command-line into an object with attributes. - ArgumentError -- The exception raised by ArgumentParser objects when there are errors with the parser's actions. Errors raised while parsing the command-line are caught by ArgumentParser and emitted as command-line messages. - FileType -- A factory for defining types of files to be created. As the example above shows, instances of FileType are typically passed as the type= argument of add_argument() calls. - Action -- The base class for parser actions. Typically actions are selected by passing strings like 'store_true' or 'append_const' to the action= argument of add_argument(). However, for greater customization of ArgumentParser actions, subclasses of Action may be defined and passed as the action= argument. - HelpFormatter, RawDescriptionHelpFormatter, RawTextHelpFormatter, ArgumentDefaultsHelpFormatter -- Formatter classes which may be passed as the formatter_class= argument to the ArgumentParser constructor. HelpFormatter is the default, RawDescriptionHelpFormatter and RawTextHelpFormatter tell the parser not to change the formatting for help text, and ArgumentDefaultsHelpFormatter adds information about argument defaults to the help. All other classes in this module are considered implementation details. (Also note that HelpFormatter and RawDescriptionHelpFormatter are only considered public as object names -- the API of the formatter objects is still considered an implementation detail.) """ __version__ = '1.1' __all__ = [ 'ArgumentParser', 'ArgumentError', 'Namespace', 'Action', 'FileType', 'HelpFormatter', 'RawDescriptionHelpFormatter', 'RawTextHelpFormatter', 'ArgumentDefaultsHelpFormatter', ] import copy as _copy import os as _os import re as _re import sys as _sys import textwrap as _textwrap from gettext import gettext as _ try: _set = set except NameError: from sets import Set as _set try: _basestring = basestring except NameError: _basestring = str try: _sorted = sorted except NameError: def _sorted(iterable, reverse=False): result = list(iterable) result.sort() if reverse: result.reverse() return result def _callable(obj): return hasattr(obj, '__call__') or hasattr(obj, '__bases__') # silence Python 2.6 buggy warnings about Exception.message if _sys.version_info[:2] == (2, 6): import warnings warnings.filterwarnings( action='ignore', message='BaseException.message has been deprecated as of Python 2.6', category=DeprecationWarning, module='argparse') SUPPRESS = '==SUPPRESS==' OPTIONAL = '?' ZERO_OR_MORE = '*' ONE_OR_MORE = '+' PARSER = 'A...' REMAINDER = '...' # ============================= # Utility functions and classes # ============================= class _AttributeHolder(object): """Abstract base class that provides __repr__. The __repr__ method returns a string in the format:: ClassName(attr=name, attr=name, ...) The attributes are determined either by a class-level attribute, '_kwarg_names', or by inspecting the instance __dict__. """ def __repr__(self): type_name = type(self).__name__ arg_strings = [] for arg in self._get_args(): arg_strings.append(repr(arg)) for name, value in self._get_kwargs(): arg_strings.append('%s=%r' % (name, value)) return '%s(%s)' % (type_name, ', '.join(arg_strings)) def _get_kwargs(self): return _sorted(self.__dict__.items()) def _get_args(self): return [] def _ensure_value(namespace, name, value): if getattr(namespace, name, None) is None: setattr(namespace, name, value) return getattr(namespace, name) # =============== # Formatting Help # =============== class HelpFormatter(object): """Formatter for generating usage messages and argument help strings. Only the name of this class is considered a public API. All the methods provided by the class are considered an implementation detail. """ def __init__(self, prog, indent_increment=2, max_help_position=24, width=None): # default setting for width if width is None: try: width = int(_os.environ['COLUMNS']) except (KeyError, ValueError): width = 80 width -= 2 self._prog = prog self._indent_increment = indent_increment self._max_help_position = max_help_position self._width = width self._current_indent = 0 self._level = 0 self._action_max_length = 0 self._root_section = self._Section(self, None) self._current_section = self._root_section self._whitespace_matcher = _re.compile(r'\s+') self._long_break_matcher = _re.compile(r'\n\n\n+') # =============================== # Section and indentation methods # =============================== def _indent(self): self._current_indent += self._indent_increment self._level += 1 def _dedent(self): self._current_indent -= self._indent_increment assert self._current_indent >= 0, 'Indent decreased below 0.' self._level -= 1 class _Section(object): def __init__(self, formatter, parent, heading=None): self.formatter = formatter self.parent = parent self.heading = heading self.items = [] def format_help(self): # format the indented section if self.parent is not None: self.formatter._indent() join = self.formatter._join_parts for func, args in self.items: func(*args) item_help = join([func(*args) for func, args in self.items]) if self.parent is not None: self.formatter._dedent() # return nothing if the section was empty if not item_help: return '' # add the heading if the section was non-empty if self.heading is not SUPPRESS and self.heading is not None: current_indent = self.formatter._current_indent heading = '%*s%s:\n' % (current_indent, '', self.heading) else: heading = '' # join the section-initial newline, the heading and the help return join(['\n', heading, item_help, '\n']) def _add_item(self, func, args): self._current_section.items.append((func, args)) # ======================== # Message building methods # ======================== def start_section(self, heading): self._indent() section = self._Section(self, self._current_section, heading) self._add_item(section.format_help, []) self._current_section = section def end_section(self): self._current_section = self._current_section.parent self._dedent() def add_text(self, text): if text is not SUPPRESS and text is not None: self._add_item(self._format_text, [text]) def add_usage(self, usage, actions, groups, prefix=None): if usage is not SUPPRESS: args = usage, actions, groups, prefix self._add_item(self._format_usage, args) def add_argument(self, action): if action.help is not SUPPRESS: # find all invocations get_invocation = self._format_action_invocation invocations = [get_invocation(action)] for subaction in self._iter_indented_subactions(action): invocations.append(get_invocation(subaction)) # update the maximum item length invocation_length = max([len(s) for s in invocations]) action_length = invocation_length + self._current_indent self._action_max_length = max(self._action_max_length, action_length) # add the item to the list self._add_item(self._format_action, [action]) def add_arguments(self, actions): for action in actions: self.add_argument(action) # ======================= # Help-formatting methods # ======================= def format_help(self): help = self._root_section.format_help() if help: help = self._long_break_matcher.sub('\n\n', help) help = help.strip('\n') + '\n' return help def _join_parts(self, part_strings): return ''.join([part for part in part_strings if part and part is not SUPPRESS]) def _format_usage(self, usage, actions, groups, prefix): if prefix is None: prefix = _('usage: ') # if usage is specified, use that if usage is not None: usage = usage % dict(prog=self._prog) # if no optionals or positionals are available, usage is just prog elif usage is None and not actions: usage = '%(prog)s' % dict(prog=self._prog) # if optionals and positionals are available, calculate usage elif usage is None: prog = '%(prog)s' % dict(prog=self._prog) # split optionals from positionals optionals = [] positionals = [] for action in actions: if action.option_strings: optionals.append(action) else: positionals.append(action) # build full usage string format = self._format_actions_usage action_usage = format(optionals + positionals, groups) usage = ' '.join([s for s in [prog, action_usage] if s]) # wrap the usage parts if it's too long text_width = self._width - self._current_indent if len(prefix) + len(usage) > text_width: # break usage into wrappable parts part_regexp = r'\(.*?\)+|\[.*?\]+|\S+' opt_usage = format(optionals, groups) pos_usage = format(positionals, groups) opt_parts = _re.findall(part_regexp, opt_usage) pos_parts = _re.findall(part_regexp, pos_usage) assert ' '.join(opt_parts) == opt_usage assert ' '.join(pos_parts) == pos_usage # helper for wrapping lines def get_lines(parts, indent, prefix=None): lines = [] line = [] if prefix is not None: line_len = len(prefix) - 1 else: line_len = len(indent) - 1 for part in parts: if line_len + 1 + len(part) > text_width: lines.append(indent + ' '.join(line)) line = [] line_len = len(indent) - 1 line.append(part) line_len += len(part) + 1 if line: lines.append(indent + ' '.join(line)) if prefix is not None: lines[0] = lines[0][len(indent):] return lines # if prog is short, follow it with optionals or positionals if len(prefix) + len(prog) <= 0.75 * text_width: indent = ' ' * (len(prefix) + len(prog) + 1) if opt_parts: lines = get_lines([prog] + opt_parts, indent, prefix) lines.extend(get_lines(pos_parts, indent)) elif pos_parts: lines = get_lines([prog] + pos_parts, indent, prefix) else: lines = [prog] # if prog is long, put it on its own line else: indent = ' ' * len(prefix) parts = opt_parts + pos_parts lines = get_lines(parts, indent) if len(lines) > 1: lines = [] lines.extend(get_lines(opt_parts, indent)) lines.extend(get_lines(pos_parts, indent)) lines = [prog] + lines # join lines into usage usage = '\n'.join(lines) # prefix with 'usage:' return '%s%s\n\n' % (prefix, usage) def _format_actions_usage(self, actions, groups): # find group indices and identify actions in groups group_actions = _set() inserts = {} for group in groups: try: start = actions.index(group._group_actions[0]) except ValueError: continue else: end = start + len(group._group_actions) if actions[start:end] == group._group_actions: for action in group._group_actions: group_actions.add(action) if not group.required: inserts[start] = '[' inserts[end] = ']' else: inserts[start] = '(' inserts[end] = ')' for i in range(start + 1, end): inserts[i] = '|' # collect all actions format strings parts = [] for i, action in enumerate(actions): # suppressed arguments are marked with None # remove | separators for suppressed arguments if action.help is SUPPRESS: parts.append(None) if inserts.get(i) == '|': inserts.pop(i) elif inserts.get(i + 1) == '|': inserts.pop(i + 1) # produce all arg strings elif not action.option_strings: part = self._format_args(action, action.dest) # if it's in a group, strip the outer [] if action in group_actions: if part[0] == '[' and part[-1] == ']': part = part[1:-1] # add the action string to the list parts.append(part) # produce the first way to invoke the option in brackets else: option_string = action.option_strings[0] # if the Optional doesn't take a value, format is: # -s or --long if action.nargs == 0: part = '%s' % option_string # if the Optional takes a value, format is: # -s ARGS or --long ARGS else: default = action.dest.upper() args_string = self._format_args(action, default) part = '%s %s' % (option_string, args_string) # make it look optional if it's not required or in a group if not action.required and action not in group_actions: part = '[%s]' % part # add the action string to the list parts.append(part) # insert things at the necessary indices for i in _sorted(inserts, reverse=True): parts[i:i] = [inserts[i]] # join all the action items with spaces text = ' '.join([item for item in parts if item is not None]) # clean up separators for mutually exclusive groups open = r'[\[(]' close = r'[\])]' text = _re.sub(r'(%s) ' % open, r'\1', text) text = _re.sub(r' (%s)' % close, r'\1', text) text = _re.sub(r'%s *%s' % (open, close), r'', text) text = _re.sub(r'\(([^|]*)\)', r'\1', text) text = text.strip() # return the text return text def _format_text(self, text): if '%(prog)' in text: text = text % dict(prog=self._prog) text_width = self._width - self._current_indent indent = ' ' * self._current_indent return self._fill_text(text, text_width, indent) + '\n\n' def _format_action(self, action): # determine the required width and the entry label help_position = min(self._action_max_length + 2, self._max_help_position) help_width = self._width - help_position action_width = help_position - self._current_indent - 2 action_header = self._format_action_invocation(action) # ho nelp; start on same line and add a final newline if not action.help: tup = self._current_indent, '', action_header action_header = '%*s%s\n' % tup # short action name; start on the same line and pad two spaces elif len(action_header) <= action_width: tup = self._current_indent, '', action_width, action_header action_header = '%*s%-*s ' % tup indent_first = 0 # long action name; start on the next line else: tup = self._current_indent, '', action_header action_header = '%*s%s\n' % tup indent_first = help_position # collect the pieces of the action help parts = [action_header] # if there was help for the action, add lines of help text if action.help: help_text = self._expand_help(action) help_lines = self._split_lines(help_text, help_width) parts.append('%*s%s\n' % (indent_first, '', help_lines[0])) for line in help_lines[1:]: parts.append('%*s%s\n' % (help_position, '', line)) # or add a newline if the description doesn't end with one elif not action_header.endswith('\n'): parts.append('\n') # if there are any sub-actions, add their help as well for subaction in self._iter_indented_subactions(action): parts.append(self._format_action(subaction)) # return a single string return self._join_parts(parts) def _format_action_invocation(self, action): if not action.option_strings: metavar, = self._metavar_formatter(action, action.dest)(1) return metavar else: parts = [] # if the Optional doesn't take a value, format is: # -s, --long if action.nargs == 0: parts.extend(action.option_strings) # if the Optional takes a value, format is: # -s ARGS, --long ARGS else: default = action.dest.upper() args_string = self._format_args(action, default) for option_string in action.option_strings: parts.append('%s %s' % (option_string, args_string)) return ', '.join(parts) def _metavar_formatter(self, action, default_metavar): if action.metavar is not None: result = action.metavar elif action.choices is not None: choice_strs = [str(choice) for choice in action.choices] result = '{%s}' % ','.join(choice_strs) else: result = default_metavar def format(tuple_size): if isinstance(result, tuple): return result else: return (result, ) * tuple_size return format def _format_args(self, action, default_metavar): get_metavar = self._metavar_formatter(action, default_metavar) if action.nargs is None: result = '%s' % get_metavar(1) elif action.nargs == OPTIONAL: result = '[%s]' % get_metavar(1) elif action.nargs == ZERO_OR_MORE: result = '[%s [%s ...]]' % get_metavar(2) elif action.nargs == ONE_OR_MORE: result = '%s [%s ...]' % get_metavar(2) elif action.nargs == REMAINDER: result = '...' elif action.nargs == PARSER: result = '%s ...' % get_metavar(1) else: formats = ['%s' for _ in range(action.nargs)] result = ' '.join(formats) % get_metavar(action.nargs) return result def _expand_help(self, action): params = dict(vars(action), prog=self._prog) for name in list(params): if params[name] is SUPPRESS: del params[name] for name in list(params): if hasattr(params[name], '__name__'): params[name] = params[name].__name__ if params.get('choices') is not None: choices_str = ', '.join([str(c) for c in params['choices']]) params['choices'] = choices_str return self._get_help_string(action) % params def _iter_indented_subactions(self, action): try: get_subactions = action._get_subactions except AttributeError: pass else: self._indent() for subaction in get_subactions(): yield subaction self._dedent() def _split_lines(self, text, width): text = self._whitespace_matcher.sub(' ', text).strip() return _textwrap.wrap(text, width) def _fill_text(self, text, width, indent): text = self._whitespace_matcher.sub(' ', text).strip() return _textwrap.fill(text, width, initial_indent=indent, subsequent_indent=indent) def _get_help_string(self, action): return action.help class RawDescriptionHelpFormatter(HelpFormatter): """Help message formatter which retains any formatting in descriptions. Only the name of this class is considered a public API. All the methods provided by the class are considered an implementation detail. """ def _fill_text(self, text, width, indent): return ''.join([indent + line for line in text.splitlines(True)]) class RawTextHelpFormatter(RawDescriptionHelpFormatter): """Help message formatter which retains formatting of all help text. Only the name of this class is considered a public API. All the methods provided by the class are considered an implementation detail. """ def _split_lines(self, text, width): return text.splitlines() class ArgumentDefaultsHelpFormatter(HelpFormatter): """Help message formatter which adds default values to argument help. Only the name of this class is considered a public API. All the methods provided by the class are considered an implementation detail. """ def _get_help_string(self, action): help = action.help if '%(default)' not in action.help: if action.default is not SUPPRESS: defaulting_nargs = [OPTIONAL, ZERO_OR_MORE] if action.option_strings or action.nargs in defaulting_nargs: help += ' (default: %(default)s)' return help # ===================== # Options and Arguments # ===================== def _get_action_name(argument): if argument is None: return None elif argument.option_strings: return '/'.join(argument.option_strings) elif argument.metavar not in (None, SUPPRESS): return argument.metavar elif argument.dest not in (None, SUPPRESS): return argument.dest else: return None class ArgumentError(Exception): """An error from creating or using an argument (optional or positional). The string value of this exception is the message, augmented with information about the argument that caused it. """ def __init__(self, argument, message): self.argument_name = _get_action_name(argument) self.message = message def __str__(self): if self.argument_name is None: format = '%(message)s' else: format = 'argument %(argument_name)s: %(message)s' return format % dict(message=self.message, argument_name=self.argument_name) class ArgumentTypeError(Exception): """An error from trying to convert a command line string to a type.""" pass # ============== # Action classes # ============== class Action(_AttributeHolder): """Information about how to convert command line strings to Python objects. Action objects are used by an ArgumentParser to represent the information needed to parse a single argument from one or more strings from the command line. The keyword arguments to the Action constructor are also all attributes of Action instances. Keyword Arguments: - option_strings -- A list of command-line option strings which should be associated with this action. - dest -- The name of the attribute to hold the created object(s) - nargs -- The number of command-line arguments that should be consumed. By default, one argument will be consumed and a single value will be produced. Other values include: - N (an integer) consumes N arguments (and produces a list) - '?' consumes zero or one arguments - '*' consumes zero or more arguments (and produces a list) - '+' consumes one or more arguments (and produces a list) Note that the difference between the default and nargs=1 is that with the default, a single value will be produced, while with nargs=1, a list containing a single value will be produced. - const -- The value to be produced if the option is specified and the option uses an action that takes no values. - default -- The value to be produced if the option is not specified. - type -- The type which the command-line arguments should be converted to, should be one of 'string', 'int', 'float', 'complex' or a callable object that accepts a single string argument. If None, 'string' is assumed. - choices -- A container of values that should be allowed. If not None, after a command-line argument has been converted to the appropriate type, an exception will be raised if it is not a member of this collection. - required -- True if the action must always be specified at the command line. This is only meaningful for optional command-line arguments. - help -- The help string describing the argument. - metavar -- The name to be used for the option's argument with the help string. If None, the 'dest' value will be used as the name. """ def __init__(self, option_strings, dest, nargs=None, const=None, default=None, type=None, choices=None, required=False, help=None, metavar=None): self.option_strings = option_strings self.dest = dest self.nargs = nargs self.const = const self.default = default self.type = type self.choices = choices self.required = required self.help = help self.metavar = metavar def _get_kwargs(self): names = [ 'option_strings', 'dest', 'nargs', 'const', 'default', 'type', 'choices', 'help', 'metavar', ] return [(name, getattr(self, name)) for name in names] def __call__(self, parser, namespace, values, option_string=None): raise NotImplementedError(_('.__call__() not defined')) class _StoreAction(Action): def __init__(self, option_strings, dest, nargs=None, const=None, default=None, type=None, choices=None, required=False, help=None, metavar=None): if nargs == 0: raise ValueError('nargs for store actions must be > 0; if you ' 'have nothing to store, actions such as store ' 'true or store const may be more appropriate') if const is not None and nargs != OPTIONAL: raise ValueError('nargs must be %r to supply const' % OPTIONAL) super(_StoreAction, self).__init__( option_strings=option_strings, dest=dest, nargs=nargs, const=const, default=default, type=type, choices=choices, required=required, help=help, metavar=metavar) def __call__(self, parser, namespace, values, option_string=None): setattr(namespace, self.dest, values) class _StoreConstAction(Action): def __init__(self, option_strings, dest, const, default=None, required=False, help=None, metavar=None): super(_StoreConstAction, self).__init__( option_strings=option_strings, dest=dest, nargs=0, const=const, default=default, required=required, help=help) def __call__(self, parser, namespace, values, option_string=None): setattr(namespace, self.dest, self.const) class _StoreTrueAction(_StoreConstAction): def __init__(self, option_strings, dest, default=False, required=False, help=None): super(_StoreTrueAction, self).__init__( option_strings=option_strings, dest=dest, const=True, default=default, required=required, help=help) class _StoreFalseAction(_StoreConstAction): def __init__(self, option_strings, dest, default=True, required=False, help=None): super(_StoreFalseAction, self).__init__( option_strings=option_strings, dest=dest, const=False, default=default, required=required, help=help) class _AppendAction(Action): def __init__(self, option_strings, dest, nargs=None, const=None, default=None, type=None, choices=None, required=False, help=None, metavar=None): if nargs == 0: raise ValueError('nargs for append actions must be > 0; if arg ' 'strings are not supplying the value to append, ' 'the append const action may be more appropriate') if const is not None and nargs != OPTIONAL: raise ValueError('nargs must be %r to supply const' % OPTIONAL) super(_AppendAction, self).__init__( option_strings=option_strings, dest=dest, nargs=nargs, const=const, default=default, type=type, choices=choices, required=required, help=help, metavar=metavar) def __call__(self, parser, namespace, values, option_string=None): items = _copy.copy(_ensure_value(namespace, self.dest, [])) items.append(values) setattr(namespace, self.dest, items) class _AppendConstAction(Action): def __init__(self, option_strings, dest, const, default=None, required=False, help=None, metavar=None): super(_AppendConstAction, self).__init__( option_strings=option_strings, dest=dest, nargs=0, const=const, default=default, required=required, help=help, metavar=metavar) def __call__(self, parser, namespace, values, option_string=None): items = _copy.copy(_ensure_value(namespace, self.dest, [])) items.append(self.const) setattr(namespace, self.dest, items) class _CountAction(Action): def __init__(self, option_strings, dest, default=None, required=False, help=None): super(_CountAction, self).__init__( option_strings=option_strings, dest=dest, nargs=0, default=default, required=required, help=help) def __call__(self, parser, namespace, values, option_string=None): new_count = _ensure_value(namespace, self.dest, 0) + 1 setattr(namespace, self.dest, new_count) class _HelpAction(Action): def __init__(self, option_strings, dest=SUPPRESS, default=SUPPRESS, help=None): super(_HelpAction, self).__init__( option_strings=option_strings, dest=dest, default=default, nargs=0, help=help) def __call__(self, parser, namespace, values, option_string=None): parser.print_help() parser.exit() class _VersionAction(Action): def __init__(self, option_strings, version=None, dest=SUPPRESS, default=SUPPRESS, help=None): super(_VersionAction, self).__init__( option_strings=option_strings, dest=dest, default=default, nargs=0, help=help) self.version = version def __call__(self, parser, namespace, values, option_string=None): version = self.version if version is None: version = parser.version formatter = parser._get_formatter() formatter.add_text(version) parser.exit(message=formatter.format_help()) class _SubParsersAction(Action): class _ChoicesPseudoAction(Action): def __init__(self, name, help): sup = super(_SubParsersAction._ChoicesPseudoAction, self) sup.__init__(option_strings=[], dest=name, help=help) def __init__(self, option_strings, prog, parser_class, dest=SUPPRESS, help=None, metavar=None): self._prog_prefix = prog self._parser_class = parser_class self._name_parser_map = {} self._choices_actions = [] super(_SubParsersAction, self).__init__( option_strings=option_strings, dest=dest, nargs=PARSER, choices=self._name_parser_map, help=help, metavar=metavar) def add_parser(self, name, **kwargs): # set prog from the existing prefix if kwargs.get('prog') is None: kwargs['prog'] = '%s %s' % (self._prog_prefix, name) # create a pseudo-action to hold the choice help if 'help' in kwargs: help = kwargs.pop('help') choice_action = self._ChoicesPseudoAction(name, help) self._choices_actions.append(choice_action) # create the parser and add it to the map parser = self._parser_class(**kwargs) self._name_parser_map[name] = parser return parser def _get_subactions(self): return self._choices_actions def __call__(self, parser, namespace, values, option_string=None): parser_name = values[0] arg_strings = values[1:] # set the parser name if requested if self.dest is not SUPPRESS: setattr(namespace, self.dest, parser_name) # select the parser try: parser = self._name_parser_map[parser_name] except KeyError: tup = parser_name, ', '.join(self._name_parser_map) msg = _('unknown parser %r (choices: %s)' % tup) raise ArgumentError(self, msg) # parse all the remaining options into the namespace parser.parse_args(arg_strings, namespace) # ============== # Type classes # ============== class FileType(object): """Factory for creating file object types Instances of FileType are typically passed as type= arguments to the ArgumentParser add_argument() method. Keyword Arguments: - mode -- A string indicating how the file is to be opened. Accepts the same values as the builtin open() function. - bufsize -- The file's desired buffer size. Accepts the same values as the builtin open() function. """ def __init__(self, mode='r', bufsize=None): self._mode = mode self._bufsize = bufsize def __call__(self, string): # the special argument "-" means sys.std{in,out} if string == '-': if 'r' in self._mode: return _sys.stdin elif 'w' in self._mode: return _sys.stdout else: msg = _('argument "-" with mode %r' % self._mode) raise ValueError(msg) # all other arguments are used as file names if self._bufsize: return open(string, self._mode, self._bufsize) else: return open(string, self._mode) def __repr__(self): args = [self._mode, self._bufsize] args_str = ', '.join([repr(arg) for arg in args if arg is not None]) return '%s(%s)' % (type(self).__name__, args_str) # =========================== # Optional and Positional Parsing # =========================== class Namespace(_AttributeHolder): """Simple object for storing attributes. Implements equality by attribute names and values, and provides a simple string representation. """ def __init__(self, **kwargs): for name in kwargs: setattr(self, name, kwargs[name]) def __eq__(self, other): return vars(self) == vars(other) def __ne__(self, other): return not (self == other) def __contains__(self, key): return key in self.__dict__ class _ActionsContainer(object): def __init__(self, description, prefix_chars, argument_default, conflict_handler): super(_ActionsContainer, self).__init__() self.description = description self.argument_default = argument_default self.prefix_chars = prefix_chars self.conflict_handler = conflict_handler # set up registries self._registries = {} # register actions self.register('action', None, _StoreAction) self.register('action', 'store', _StoreAction) self.register('action', 'store_const', _StoreConstAction) self.register('action', 'store_true', _StoreTrueAction) self.register('action', 'store_false', _StoreFalseAction) self.register('action', 'append', _AppendAction) self.register('action', 'append_const', _AppendConstAction) self.register('action', 'count', _CountAction) self.register('action', 'help', _HelpAction) self.register('action', 'version', _VersionAction) self.register('action', 'parsers', _SubParsersAction) # raise an exception if the conflict handler is invalid self._get_handler() # action storage self._actions = [] self._option_string_actions = {} # groups self._action_groups = [] self._mutually_exclusive_groups = [] # defaults storage self._defaults = {} # determines whether an "option" looks like a negative number self._negative_number_matcher = _re.compile(r'^-\d+$|^-\d*\.\d+$') # whether or not there are any optionals that look like negative # numbers -- uses a list so it can be shared and edited self._has_negative_number_optionals = [] # ==================== # Registration methods # ==================== def register(self, registry_name, value, object): registry = self._registries.setdefault(registry_name, {}) registry[value] = object def _registry_get(self, registry_name, value, default=None): return self._registries[registry_name].get(value, default) # ================================== # Namespace default accessor methods # ================================== def set_defaults(self, **kwargs): self._defaults.update(kwargs) # if these defaults match any existing arguments, replace # the previous default on the object with the new one for action in self._actions: if action.dest in kwargs: action.default = kwargs[action.dest] def get_default(self, dest): for action in self._actions: if action.dest == dest and action.default is not None: return action.default return self._defaults.get(dest, None) # ======================= # Adding argument actions # ======================= def add_argument(self, *args, **kwargs): """ add_argument(dest, ..., name=value, ...) add_argument(option_string, option_string, ..., name=value, ...) """ # if no positional args are supplied or only one is supplied and # it doesn't look like an option string, parse a positional # argument chars = self.prefix_chars if not args or len(args) == 1 and args[0][0] not in chars: if args and 'dest' in kwargs: raise ValueError('dest supplied twice for positional argument') kwargs = self._get_positional_kwargs(*args, **kwargs) # otherwise, we're adding an optional argument else: kwargs = self._get_optional_kwargs(*args, **kwargs) # if no default was supplied, use the parser-level default if 'default' not in kwargs: dest = kwargs['dest'] if dest in self._defaults: kwargs['default'] = self._defaults[dest] elif self.argument_default is not None: kwargs['default'] = self.argument_default # create the action object, and add it to the parser action_class = self._pop_action_class(kwargs) if not _callable(action_class): raise ValueError('unknown action "%s"' % action_class) action = action_class(**kwargs) # raise an error if the action type is not callable type_func = self._registry_get('type', action.type, action.type) if not _callable(type_func): raise ValueError('%r is not callable' % type_func) return self._add_action(action) def add_argument_group(self, *args, **kwargs): group = _ArgumentGroup(self, *args, **kwargs) self._action_groups.append(group) return group def add_mutually_exclusive_group(self, **kwargs): group = _MutuallyExclusiveGroup(self, **kwargs) self._mutually_exclusive_groups.append(group) return group def _add_action(self, action): # resolve any conflicts self._check_conflict(action) # add to actions list self._actions.append(action) action.container = self # index the action by any option strings it has for option_string in action.option_strings: self._option_string_actions[option_string] = action # set the flag if any option strings look like negative numbers for option_string in action.option_strings: if self._negative_number_matcher.match(option_string): if not self._has_negative_number_optionals: self._has_negative_number_optionals.append(True) # return the created action return action def _remove_action(self, action): self._actions.remove(action) def _add_container_actions(self, container): # collect groups by titles title_group_map = {} for group in self._action_groups: if group.title in title_group_map: msg = _('cannot merge actions - two groups are named %r') raise ValueError(msg % (group.title)) title_group_map[group.title] = group # map each action to its group group_map = {} for group in container._action_groups: # if a group with the title exists, use that, otherwise # create a new group matching the container's group if group.title not in title_group_map: title_group_map[group.title] = self.add_argument_group( title=group.title, description=group.description, conflict_handler=group.conflict_handler) # map the actions to their new group for action in group._group_actions: group_map[action] = title_group_map[group.title] # add container's mutually exclusive groups # NOTE: if add_mutually_exclusive_group ever gains title= and # description= then this code will need to be expanded as above for group in container._mutually_exclusive_groups: mutex_group = self.add_mutually_exclusive_group( required=group.required) # map the actions to their new mutex group for action in group._group_actions: group_map[action] = mutex_group # add all actions to this container or their group for action in container._actions: group_map.get(action, self)._add_action(action) def _get_positional_kwargs(self, dest, **kwargs): # make sure required is not specified if 'required' in kwargs: msg = _("'required' is an invalid argument for positionals") raise TypeError(msg) # mark positional arguments as required if at least one is # always required if kwargs.get('nargs') not in [OPTIONAL, ZERO_OR_MORE]: kwargs['required'] = True if kwargs.get('nargs') == ZERO_OR_MORE and 'default' not in kwargs: kwargs['required'] = True # return the keyword arguments with no option strings return dict(kwargs, dest=dest, option_strings=[]) def _get_optional_kwargs(self, *args, **kwargs): # determine short and long option strings option_strings = [] long_option_strings = [] for option_string in args: # error on strings that don't start with an appropriate prefix if not option_string[0] in self.prefix_chars: msg = _('invalid option string %r: ' 'must start with a character %r') tup = option_string, self.prefix_chars raise ValueError(msg % tup) # strings starting with two prefix characters are long options option_strings.append(option_string) if option_string[0] in self.prefix_chars: if len(option_string) > 1: if option_string[1] in self.prefix_chars: long_option_strings.append(option_string) # infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x' dest = kwargs.pop('dest', None) if dest is None: if long_option_strings: dest_option_string = long_option_strings[0] else: dest_option_string = option_strings[0] dest = dest_option_string.lstrip(self.prefix_chars) if not dest: msg = _('dest= is required for options like %r') raise ValueError(msg % option_string) dest = dest.replace('-', '_') # return the updated keyword arguments return dict(kwargs, dest=dest, option_strings=option_strings) def _pop_action_class(self, kwargs, default=None): action = kwargs.pop('action', default) return self._registry_get('action', action, action) def _get_handler(self): # determine function from conflict handler string handler_func_name = '_handle_conflict_%s' % self.conflict_handler try: return getattr(self, handler_func_name) except AttributeError: msg = _('invalid conflict_resolution value: %r') raise ValueError(msg % self.conflict_handler) def _check_conflict(self, action): # find all options that conflict with this option confl_optionals = [] for option_string in action.option_strings: if option_string in self._option_string_actions: confl_optional = self._option_string_actions[option_string] confl_optionals.append((option_string, confl_optional)) # resolve any conflicts if confl_optionals: conflict_handler = self._get_handler() conflict_handler(action, confl_optionals) def _handle_conflict_error(self, action, conflicting_actions): message = _('conflicting option string(s): %s') conflict_string = ', '.join([option_string for option_string, action in conflicting_actions]) raise ArgumentError(action, message % conflict_string) def _handle_conflict_resolve(self, action, conflicting_actions): # remove all conflicting options for option_string, action in conflicting_actions: # remove the conflicting option action.option_strings.remove(option_string) self._option_string_actions.pop(option_string, None) # if the option now has no option string, remove it from the # container holding it if not action.option_strings: action.container._remove_action(action) class _ArgumentGroup(_ActionsContainer): def __init__(self, container, title=None, description=None, **kwargs): # add any missing keyword arguments by checking the container update = kwargs.setdefault update('conflict_handler', container.conflict_handler) update('prefix_chars', container.prefix_chars) update('argument_default', container.argument_default) super_init = super(_ArgumentGroup, self).__init__ super_init(description=description, **kwargs) # group attributes self.title = title self._group_actions = [] # share most attributes with the container self._registries = container._registries self._actions = container._actions self._option_string_actions = container._option_string_actions self._defaults = container._defaults self._has_negative_number_optionals = \ container._has_negative_number_optionals def _add_action(self, action): action = super(_ArgumentGroup, self)._add_action(action) self._group_actions.append(action) return action def _remove_action(self, action): super(_ArgumentGroup, self)._remove_action(action) self._group_actions.remove(action) class _MutuallyExclusiveGroup(_ArgumentGroup): def __init__(self, container, required=False): super(_MutuallyExclusiveGroup, self).__init__(container) self.required = required self._container = container def _add_action(self, action): if action.required: msg = _('mutually exclusive arguments must be optional') raise ValueError(msg) action = self._container._add_action(action) self._group_actions.append(action) return action def _remove_action(self, action): self._container._remove_action(action) self._group_actions.remove(action) class ArgumentParser(_AttributeHolder, _ActionsContainer): """Object for parsing command line strings into Python objects. Keyword Arguments: - prog -- The name of the program (default: sys.argv[0]) - usage -- A usage message (default: auto-generated from arguments) - description -- A description of what the program does - epilog -- Text following the argument descriptions - parents -- Parsers whose arguments should be copied into this one - formatter_class -- HelpFormatter class for printing help messages - prefix_chars -- Characters that prefix optional arguments - fromfile_prefix_chars -- Characters that prefix files containing additional arguments - argument_default -- The default value for all arguments - conflict_handler -- String indicating how to handle conflicts - add_help -- Add a -h/-help option """ def __init__(self, prog=None, usage=None, description=None, epilog=None, version=None, parents=[], formatter_class=HelpFormatter, prefix_chars='-', fromfile_prefix_chars=None, argument_default=None, conflict_handler='error', add_help=True): if version is not None: import warnings warnings.warn( """The "version" argument to ArgumentParser is deprecated. """ """Please use """ """"add_argument(..., action='version', version="N", ...)" """ """instead""", DeprecationWarning) superinit = super(ArgumentParser, self).__init__ superinit(description=description, prefix_chars=prefix_chars, argument_default=argument_default, conflict_handler=conflict_handler) # default setting for prog if prog is None: prog = _os.path.basename(_sys.argv[0]) self.prog = prog self.usage = usage self.epilog = epilog self.version = version self.formatter_class = formatter_class self.fromfile_prefix_chars = fromfile_prefix_chars self.add_help = add_help add_group = self.add_argument_group self._positionals = add_group(_('positional arguments')) self._optionals = add_group(_('optional arguments')) self._subparsers = None # register types def identity(string): return string self.register('type', None, identity) # add help and version arguments if necessary # (using explicit default to override global argument_default) if self.add_help: self.add_argument( '-h', '--help', action='help', default=SUPPRESS, help=_('show this help message and exit')) if self.version: self.add_argument( '-v', '--version', action='version', default=SUPPRESS, version=self.version, help=_("show program's version number and exit")) # add parent arguments and defaults for parent in parents: self._add_container_actions(parent) try: defaults = parent._defaults except AttributeError: pass else: self._defaults.update(defaults) # ======================= # Pretty __repr__ methods # ======================= def _get_kwargs(self): names = [ 'prog', 'usage', 'description', 'version', 'formatter_class', 'conflict_handler', 'add_help', ] return [(name, getattr(self, name)) for name in names] # ================================== # Optional/Positional adding methods # ================================== def add_subparsers(self, **kwargs): if self._subparsers is not None: self.error(_('cannot have multiple subparser arguments')) # add the parser class to the arguments if it's not present kwargs.setdefault('parser_class', type(self)) if 'title' in kwargs or 'description' in kwargs: title = _(kwargs.pop('title', 'subcommands')) description = _(kwargs.pop('description', None)) self._subparsers = self.add_argument_group(title, description) else: self._subparsers = self._positionals # prog defaults to the usage message of this parser, skipping # optional arguments and with no "usage:" prefix if kwargs.get('prog') is None: formatter = self._get_formatter() positionals = self._get_positional_actions() groups = self._mutually_exclusive_groups formatter.add_usage(self.usage, positionals, groups, '') kwargs['prog'] = formatter.format_help().strip() # create the parsers action and add it to the positionals list parsers_class = self._pop_action_class(kwargs, 'parsers') action = parsers_class(option_strings=[], **kwargs) self._subparsers._add_action(action) # return the created parsers action return action def _add_action(self, action): if action.option_strings: self._optionals._add_action(action) else: self._positionals._add_action(action) return action def _get_optional_actions(self): return [action for action in self._actions if action.option_strings] def _get_positional_actions(self): return [action for action in self._actions if not action.option_strings] # ===================================== # Command line argument parsing methods # ===================================== def parse_args(self, args=None, namespace=None): args, argv = self.parse_known_args(args, namespace) if argv: msg = _('unrecognized arguments: %s') self.error(msg % ' '.join(argv)) return args def parse_known_args(self, args=None, namespace=None): # args default to the system args if args is None: args = _sys.argv[1:] # default Namespace built from parser defaults if namespace is None: namespace = Namespace() # add any action defaults that aren't present for action in self._actions: if action.dest is not SUPPRESS: if not hasattr(namespace, action.dest): if action.default is not SUPPRESS: default = action.default if isinstance(action.default, _basestring): default = self._get_value(action, default) setattr(namespace, action.dest, default) # add any parser defaults that aren't present for dest in self._defaults: if not hasattr(namespace, dest): setattr(namespace, dest, self._defaults[dest]) # parse the arguments and exit if there are any errors try: return self._parse_known_args(args, namespace) except ArgumentError: err = _sys.exc_info()[1] self.error(str(err)) def _parse_known_args(self, arg_strings, namespace): # replace arg strings that are file references if self.fromfile_prefix_chars is not None: arg_strings = self._read_args_from_files(arg_strings) # map all mutually exclusive arguments to the other arguments # they can't occur with action_conflicts = {} for mutex_group in self._mutually_exclusive_groups: group_actions = mutex_group._group_actions for i, mutex_action in enumerate(mutex_group._group_actions): conflicts = action_conflicts.setdefault(mutex_action, []) conflicts.extend(group_actions[:i]) conflicts.extend(group_actions[i + 1:]) # find all option indices, and determine the arg_string_pattern # which has an 'O' if there is an option at an index, # an 'A' if there is an argument, or a '-' if there is a '--' option_string_indices = {} arg_string_pattern_parts = [] arg_strings_iter = iter(arg_strings) for i, arg_string in enumerate(arg_strings_iter): # all args after -- are non-options if arg_string == '--': arg_string_pattern_parts.append('-') for arg_string in arg_strings_iter: arg_string_pattern_parts.append('A') # otherwise, add the arg to the arg strings # and note the index if it was an option else: option_tuple = self._parse_optional(arg_string) if option_tuple is None: pattern = 'A' else: option_string_indices[i] = option_tuple pattern = 'O' arg_string_pattern_parts.append(pattern) # join the pieces together to form the pattern arg_strings_pattern = ''.join(arg_string_pattern_parts) # converts arg strings to the appropriate and then takes the action seen_actions = _set() seen_non_default_actions = _set() def take_action(action, argument_strings, option_string=None): seen_actions.add(action) argument_values = self._get_values(action, argument_strings) # error if this argument is not allowed with other previously # seen arguments, assuming that actions that use the default # value don't really count as "present" if argument_values is not action.default: seen_non_default_actions.add(action) for conflict_action in action_conflicts.get(action, []): if conflict_action in seen_non_default_actions: msg = _('not allowed with argument %s') action_name = _get_action_name(conflict_action) raise ArgumentError(action, msg % action_name) # take the action if we didn't receive a SUPPRESS value # (e.g. from a default) if argument_values is not SUPPRESS: action(self, namespace, argument_values, option_string) # function to convert arg_strings into an optional action def consume_optional(start_index): # get the optional identified at this index option_tuple = option_string_indices[start_index] action, option_string, explicit_arg = option_tuple # identify additional optionals in the same arg string # (e.g. -xyz is the same as -x -y -z if no args are required) match_argument = self._match_argument action_tuples = [] while True: # if we found no optional action, skip it if action is None: extras.append(arg_strings[start_index]) return start_index + 1 # if there is an explicit argument, try to match the # optional's string arguments to only this if explicit_arg is not None: arg_count = match_argument(action, 'A') # if the action is a single-dash option and takes no # arguments, try to parse more single-dash options out # of the tail of the option string chars = self.prefix_chars if arg_count == 0 and option_string[1] not in chars: action_tuples.append((action, [], option_string)) for char in self.prefix_chars: option_string = char + explicit_arg[0] explicit_arg = explicit_arg[1:] or None optionals_map = self._option_string_actions if option_string in optionals_map: action = optionals_map[option_string] break else: msg = _('ignored explicit argument %r') raise ArgumentError(action, msg % explicit_arg) # if the action expect exactly one argument, we've # successfully matched the option; exit the loop elif arg_count == 1: stop = start_index + 1 args = [explicit_arg] action_tuples.append((action, args, option_string)) break # error if a double-dash option did not use the # explicit argument else: msg = _('ignored explicit argument %r') raise ArgumentError(action, msg % explicit_arg) # if there is no explicit argument, try to match the # optional's string arguments with the following strings # if successful, exit the loop else: start = start_index + 1 selected_patterns = arg_strings_pattern[start:] arg_count = match_argument(action, selected_patterns) stop = start + arg_count args = arg_strings[start:stop] action_tuples.append((action, args, option_string)) break # add the Optional to the list and return the index at which # the Optional's string args stopped assert action_tuples for action, args, option_string in action_tuples: take_action(action, args, option_string) return stop # the list of Positionals left to be parsed; this is modified # by consume_positionals() positionals = self._get_positional_actions() # function to convert arg_strings into positional actions def consume_positionals(start_index): # match as many Positionals as possible match_partial = self._match_arguments_partial selected_pattern = arg_strings_pattern[start_index:] arg_counts = match_partial(positionals, selected_pattern) # slice off the appropriate arg strings for each Positional # and add the Positional and its args to the list for action, arg_count in zip(positionals, arg_counts): args = arg_strings[start_index: start_index + arg_count] start_index += arg_count take_action(action, args) # slice off the Positionals that we just parsed and return the # index at which the Positionals' string args stopped positionals[:] = positionals[len(arg_counts):] return start_index # consume Positionals and Optionals alternately, until we have # passed the last option string extras = [] start_index = 0 if option_string_indices: max_option_string_index = max(option_string_indices) else: max_option_string_index = -1 while start_index <= max_option_string_index: # consume any Positionals preceding the next option next_option_string_index = min([ index for index in option_string_indices if index >= start_index]) if start_index != next_option_string_index: positionals_end_index = consume_positionals(start_index) # only try to parse the next optional if we didn't consume # the option string during the positionals parsing if positionals_end_index > start_index: start_index = positionals_end_index continue else: start_index = positionals_end_index # if we consumed all the positionals we could and we're not # at the index of an option string, there were extra arguments if start_index not in option_string_indices: strings = arg_strings[start_index:next_option_string_index] extras.extend(strings) start_index = next_option_string_index # consume the next optional and any arguments for it start_index = consume_optional(start_index) # consume any positionals following the last Optional stop_index = consume_positionals(start_index) # if we didn't consume all the argument strings, there were extras extras.extend(arg_strings[stop_index:]) # if we didn't use all the Positional objects, there were too few # arg strings supplied. if positionals: self.error(_('too few arguments')) # make sure all required actions were present for action in self._actions: if action.required: if action not in seen_actions: name = _get_action_name(action) self.error(_('argument %s is required') % name) # make sure all required groups had one option present for group in self._mutually_exclusive_groups: if group.required: for action in group._group_actions: if action in seen_non_default_actions: break # if no actions were used, report the error else: names = [_get_action_name(action) for action in group._group_actions if action.help is not SUPPRESS] msg = _('one of the arguments %s is required') self.error(msg % ' '.join(names)) # return the updated namespace and the extra arguments return namespace, extras def _read_args_from_files(self, arg_strings): # expand arguments referencing files new_arg_strings = [] for arg_string in arg_strings: # for regular arguments, just add them back into the list if arg_string[0] not in self.fromfile_prefix_chars: new_arg_strings.append(arg_string) # replace arguments referencing files with the file content else: try: args_file = open(arg_string[1:]) try: arg_strings = [] for arg_line in args_file.read().splitlines(): for arg in self.convert_arg_line_to_args(arg_line): arg_strings.append(arg) arg_strings = self._read_args_from_files(arg_strings) new_arg_strings.extend(arg_strings) finally: args_file.close() except IOError: err = _sys.exc_info()[1] self.error(str(err)) # return the modified argument list return new_arg_strings def convert_arg_line_to_args(self, arg_line): return [arg_line] def _match_argument(self, action, arg_strings_pattern): # match the pattern for this action to the arg strings nargs_pattern = self._get_nargs_pattern(action) match = _re.match(nargs_pattern, arg_strings_pattern) # raise an exception if we weren't able to find a match if match is None: nargs_errors = { None: _('expected one argument'), OPTIONAL: _('expected at most one argument'), ONE_OR_MORE: _('expected at least one argument'), } default = _('expected %s argument(s)') % action.nargs msg = nargs_errors.get(action.nargs, default) raise ArgumentError(action, msg) # return the number of arguments matched return len(match.group(1)) def _match_arguments_partial(self, actions, arg_strings_pattern): # progressively shorten the actions list by slicing off the # final actions until we find a match result = [] for i in range(len(actions), 0, -1): actions_slice = actions[:i] pattern = ''.join([self._get_nargs_pattern(action) for action in actions_slice]) match = _re.match(pattern, arg_strings_pattern) if match is not None: result.extend([len(string) for string in match.groups()]) break # return the list of arg string counts return result def _parse_optional(self, arg_string): # if it's an empty string, it was meant to be a positional if not arg_string: return None # if it doesn't start with a prefix, it was meant to be positional if not arg_string[0] in self.prefix_chars: return None # if the option string is present in the parser, return the action if arg_string in self._option_string_actions: action = self._option_string_actions[arg_string] return action, arg_string, None # if it's just a single character, it was meant to be positional if len(arg_string) == 1: return None # if the option string before the "=" is present, return the action if '=' in arg_string: option_string, explicit_arg = arg_string.split('=', 1) if option_string in self._option_string_actions: action = self._option_string_actions[option_string] return action, option_string, explicit_arg # search through all possible prefixes of the option string # and all actions in the parser for possible interpretations option_tuples = self._get_option_tuples(arg_string) # if multiple actions match, the option string was ambiguous if len(option_tuples) > 1: options = ', '.join([option_string for action, option_string, explicit_arg in option_tuples]) tup = arg_string, options self.error(_('ambiguous option: %s could match %s') % tup) # if exactly one action matched, this segmentation is good, # so return the parsed action elif len(option_tuples) == 1: option_tuple, = option_tuples return option_tuple # if it was not found as an option, but it looks like a negative # number, it was meant to be positional # unless there are negative-number-like options if self._negative_number_matcher.match(arg_string): if not self._has_negative_number_optionals: return None # if it contains a space, it was meant to be a positional if ' ' in arg_string: return None # it was meant to be an optional but there is no such option # in this parser (though it might be a valid option in a subparser) return None, arg_string, None def _get_option_tuples(self, option_string): result = [] # option strings starting with two prefix characters are only # split at the '=' chars = self.prefix_chars if option_string[0] in chars and option_string[1] in chars: if '=' in option_string: option_prefix, explicit_arg = option_string.split('=', 1) else: option_prefix = option_string explicit_arg = None for option_string in self._option_string_actions: if option_string.startswith(option_prefix): action = self._option_string_actions[option_string] tup = action, option_string, explicit_arg result.append(tup) # single character options can be concatenated with their arguments # but multiple character options always have to have their argument # separate elif option_string[0] in chars and option_string[1] not in chars: option_prefix = option_string explicit_arg = None short_option_prefix = option_string[:2] short_explicit_arg = option_string[2:] for option_string in self._option_string_actions: if option_string == short_option_prefix: action = self._option_string_actions[option_string] tup = action, option_string, short_explicit_arg result.append(tup) elif option_string.startswith(option_prefix): action = self._option_string_actions[option_string] tup = action, option_string, explicit_arg result.append(tup) # shouldn't ever get here else: self.error(_('unexpected option string: %s') % option_string) # return the collected option tuples return result def _get_nargs_pattern(self, action): # in all examples below, we have to allow for '--' args # which are represented as '-' in the pattern nargs = action.nargs # the default (None) is assumed to be a single argument if nargs is None: nargs_pattern = '(-*A-*)' # allow zero or one arguments elif nargs == OPTIONAL: nargs_pattern = '(-*A?-*)' # allow zero or more arguments elif nargs == ZERO_OR_MORE: nargs_pattern = '(-*[A-]*)' # allow one or more arguments elif nargs == ONE_OR_MORE: nargs_pattern = '(-*A[A-]*)' # allow any number of options or arguments elif nargs == REMAINDER: nargs_pattern = '([-AO]*)' # allow one argument followed by any number of options or arguments elif nargs == PARSER: nargs_pattern = '(-*A[-AO]*)' # all others should be integers else: nargs_pattern = '(-*%s-*)' % '-*'.join('A' * nargs) # if this is an optional action, -- is not allowed if action.option_strings: nargs_pattern = nargs_pattern.replace('-*', '') nargs_pattern = nargs_pattern.replace('-', '') # return the pattern return nargs_pattern # ======================== # Value conversion methods # ======================== def _get_values(self, action, arg_strings): # for everything but PARSER args, strip out '--' if action.nargs not in [PARSER, REMAINDER]: arg_strings = [s for s in arg_strings if s != '--'] # optional argument produces a default when not present if not arg_strings and action.nargs == OPTIONAL: if action.option_strings: value = action.const else: value = action.default if isinstance(value, _basestring): value = self._get_value(action, value) self._check_value(action, value) # when nargs='*' on a positional, if there were no command-line # args, use the default if it is anything other than None elif (not arg_strings and action.nargs == ZERO_OR_MORE and not action.option_strings): if action.default is not None: value = action.default else: value = arg_strings self._check_value(action, value) # single argument or optional argument produces a single value elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]: arg_string, = arg_strings value = self._get_value(action, arg_string) self._check_value(action, value) # REMAINDER arguments convert all values, checking none elif action.nargs == REMAINDER: value = [self._get_value(action, v) for v in arg_strings] # PARSER arguments convert all values, but check only the first elif action.nargs == PARSER: value = [self._get_value(action, v) for v in arg_strings] self._check_value(action, value[0]) # all other types of nargs produce a list else: value = [self._get_value(action, v) for v in arg_strings] for v in value: self._check_value(action, v) # return the converted value return value def _get_value(self, action, arg_string): type_func = self._registry_get('type', action.type, action.type) if not _callable(type_func): msg = _('%r is not callable') raise ArgumentError(action, msg % type_func) # convert the value to the appropriate type try: result = type_func(arg_string) # ArgumentTypeErrors indicate errors except ArgumentTypeError: name = getattr(action.type, '__name__', repr(action.type)) msg = str(_sys.exc_info()[1]) raise ArgumentError(action, msg) # TypeErrors or ValueErrors also indicate errors except (TypeError, ValueError): name = getattr(action.type, '__name__', repr(action.type)) msg = _('invalid %s value: %r') raise ArgumentError(action, msg % (name, arg_string)) # return the converted value return result def _check_value(self, action, value): # converted value must be one of the choices (if specified) if action.choices is not None and value not in action.choices: tup = value, ', '.join(map(repr, action.choices)) msg = _('invalid choice: %r (choose from %s)') % tup raise ArgumentError(action, msg) # ======================= # Help-formatting methods # ======================= def format_usage(self): formatter = self._get_formatter() formatter.add_usage(self.usage, self._actions, self._mutually_exclusive_groups) return formatter.format_help() def format_help(self): formatter = self._get_formatter() # usage formatter.add_usage(self.usage, self._actions, self._mutually_exclusive_groups) # description formatter.add_text(self.description) # positionals, optionals and user-defined groups for action_group in self._action_groups: formatter.start_section(action_group.title) formatter.add_text(action_group.description) formatter.add_arguments(action_group._group_actions) formatter.end_section() # epilog formatter.add_text(self.epilog) # determine help from format above return formatter.format_help() def format_version(self): import warnings warnings.warn( 'The format_version method is deprecated -- the "version" ' 'argument to ArgumentParser is no longer supported.', DeprecationWarning) formatter = self._get_formatter() formatter.add_text(self.version) return formatter.format_help() def _get_formatter(self): return self.formatter_class(prog=self.prog) # ===================== # Help-printing methods # ===================== def print_usage(self, file=None): if file is None: file = _sys.stdout self._print_message(self.format_usage(), file) def print_help(self, file=None): if file is None: file = _sys.stdout self._print_message(self.format_help(), file) def print_version(self, file=None): import warnings warnings.warn( 'The print_version method is deprecated -- the "version" ' 'argument to ArgumentParser is no longer supported.', DeprecationWarning) self._print_message(self.format_version(), file) def _print_message(self, message, file=None): if message: if file is None: file = _sys.stderr file.write(message) # =============== # Exiting methods # =============== def exit(self, status=0, message=None): if message: self._print_message(message, _sys.stderr) _sys.exit(status) def error(self, message): """error(message: string) Prints a usage message incorporating the message to stderr and exits. If you override this in a subclass, it should not return -- it should either exit or raise an exception. """ self.print_usage(_sys.stderr) self.exit(2, _('%s: error: %s\n') % (self.prog, message)) apt-offline-1.3.1/apt_offline_core/AptOfflineCoreLib.py0000644000000000000000000033037012157275151017761 0ustar ############################################################################ # Copyright (C) 2005, 2010 Ritesh Raj Sarraf # # rrs@researchut.com # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 3 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty 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., # # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################ import os import sys import time import shutil import platform import string import urllib2 import Queue import threading import socket import tempfile import random # to generate random directory names for installing multiple bundles in on go import zipfile import pydoc # Given the merits of argparse, I hope it'll soon be part # of the Python Standard Library. # http://code.google.com/argparse # Till then we use it this way. try: import argparse except ImportError: import AptOffline_argparse as argparse # On Debian, python-debianbts package provides this library DebianBTS = True try: import AptOfflineDebianBtsLib except ImportError: DebianBTS = False import AptOfflineMagicLib #INFO: added to handle GUI interaction guiBool = False guiTerminateSignal = False # cancelling a download guiMetaCompleted = False totalSize = [0,0] # total_size, current_total #INFO: Check if python-apt is installed PythonApt = True try: import apt import apt_pkg except ImportError: PythonApt = False PythonApt = False #Remove it after porting to python-apt import AptOfflineLib #INFO: Set the default timeout to 30 seconds for the packages that are being downloaded. socket.setdefaulttimeout(30) # How many times should we retry on socket timeouts SOCKET_TIMEOUT_RETRY = 5 '''This is the core module. It does the main job of downloading packages/update packages,\n figuring out if the packages are in the local cache, handling exceptions and many more stuff''' app_name = "apt-offline" version = "1.3" copyright = "(C) 2005 - 2013 Ritesh Raj Sarraf" terminal_license = "This program comes with ABSOLUTELY NO WARRANTY.\n\ This is free software, and you are welcome to redistribute it under\n\ the GNU GPL Version 3 (or later) License\n" errlist = [] supported_platforms = ["Linux", "GNU/kFreeBSD", "GNU"] apt_update_target_path = '/var/lib/apt/lists/partial' apt_update_final_path = '/var/lib/apt/lists/' apt_package_target_path = '/var/cache/apt/archives/' apt_bug_file_format = "__apt__bug__report" IgnoredBugTypes = ["Resolved bugs", "Normal bugs", "Minor bugs", "Wishlist items", "FIXED"] #These are spaces which will overwrite the progressbar left mess LINE_OVERWRITE_SMALL = " " * 10 LINE_OVERWRITE_MID = " " * 30 LINE_OVERWRITE_FULL = " " * 60 Bool_Verbose = False #Bool_TestWindows = True log = AptOfflineLib.Log( Bool_Verbose, lock=True ) class FetchBugReports( AptOfflineLib.Archiver ): def __init__( self, apt_bug_file_format, IgnoredBugTypes, ArchiveFile=None, lock=False ): self.bugsList = [] self.IgnoredBugTypes = IgnoredBugTypes self.lock = lock self.apt_bug = apt_bug_file_format if self.lock: AptOfflineLib.Archiver.__init__( self, lock ) self.ArchiveFile = ArchiveFile def FetchBugsDebian( self, PackageName, Filename=None ): '''0 => False 1 => No Bug Reports 2 => True''' if Filename != None: try: file_handle = open( Filename, 'a' ) except IOError: sys.exit( 1 ) try: ( num_of_bugs, header, self.bugs_list ) = AptOfflineDebianBtsLib.get_reports( PackageName ) except socket.timeout: return 0 except NoNetwork: log.verbose("Network connection to the BTS couldn't be established") return 0 if num_of_bugs: atleast_one_bug_report_downloaded = False for x in self.bugs_list: ( sub_bugs_header, sub_bugs_list ) = x #INFO: We filter all the IgnoredBugTypes that we think aren't necessary. #We don't download those low priority bug reports for BugType in self.IgnoredBugTypes: if BugType in sub_bugs_header: bug_flag = 0 break bug_flag = 1 if bug_flag: for x in sub_bugs_list: break_bugs = x.split( ' ' ) bug_num = string.lstrip( break_bugs[0], '#' ) try: data = AptOfflineDebianBtsLib.get_report( bug_num, followups=True ) except socket.timeout: break if data is None: break if Filename == None: self.fileName = PackageName + "." + bug_num + "." + self.apt_bug file_handle = open( self.fileName, 'w' ) else: self.fileName = Filename file_handle = open( self.fileName, 'a' ) file_handle.write( data[0] + "\n\n" ) for x in data[1]: file_handle.write( x ) file_handle.write( "\n" ) file_handle.write( "\n" * 3 ) file_handle.flush() file_handle.close() #We're adding to an archive file here. if self.lock: self.AddToArchive( self.ArchiveFile, self.fileName ) atleast_one_bug_report_downloaded = True if atleast_one_bug_report_downloaded: return 2 else: return 1 else: #FIXME: When no bug reports are there, i.e. bug count is 0, we hit here # We shouldn't be returning False return 1 def AddToArchive(self, ArchiveFile, fileName): if self.compress_the_file(ArchiveFile, fileName): os.unlink(fileName) return True def files(root): for path, folders, files in os.walk(root): for file in files: yield path, file def find_first_match(cache_dir=None, filename=None): '''Return the full path of the filename if a match is found Else Return False''' # Do the sanity check first #if cache_dir is None or filename is None or os.path.isdir(cache_dir) is False: if cache_dir is None: return False elif filename is None: return False elif os.path.isdir(cache_dir) is False: return False else: for path, file in files(cache_dir): if file == filename: return os.path.join(path, file) return False class GenericDownloadFunction(): def download_from_web(self, url, file, download_dir): '''url = url to fetch file = file to save to donwload_dir = download path''' try: block_size = 4096 i = 0 counter = 0 os.chdir(download_dir) temp = urllib2.urlopen(url) headers = temp.info() size = int(headers['Content-Length']) data = open(file,'wb') #INFO: Add the download thread into the Global ProgressBar Thread self.addItem(size) socket_counter = 0 while i < size: socket_timeout = None try: data.write (temp.read(block_size)) except socket.timeout, timeout: socket_timeout = True socket_counter += 1 except socket.error, error: socket_timeout = True socket_counter += 1 if socket_counter == SOCKET_TIMEOUT_RETRY: errfunc(101010, "Max timeout retry count reached. Discontinuing download.\n", url) # Clean the half downloaded file. os.unlink(file) return False if socket_timeout is True: errfunc(10054, "Socket Timeout. Retry - %d\n" % (socket_counter) , url) continue increment = min(block_size, size - i) i += block_size counter += 1 self.updateValue(increment) #REAL_PROGRESS: update current total in totalSize if guiBool and not guiTerminateSignal: totalSize[1] += block_size if guiTerminateSignal: data.close() temp.close() return False self.completed() data.close() temp.close() return True #FIXME: Find out optimal fix for this exception handling except OSError, (errno, strerror): errfunc(errno, strerror, download_dir) except urllib2.HTTPError, errstring: errfunc(errstring.code, errstring.msg, url) except urllib2.URLError, errstring: #INFO: Weird. But in urllib2.URLError, I noticed that for # error type "timeouts", no errno was defined. # errstring.errno was listed as None # In my tests, wget categorized this behavior as: # 504: gateway timeout # So I am doing the same here. if errstring.errno is None: errfunc(504, errstring.reason, url) else: errfunc(errstring.errno, errstring.reason, url) except IOError, e: if hasattr(e, 'reason'): log.err("%s\n" % (e.reason)) if hasattr(e, 'code') and hasattr(e, 'reason'): errfunc(e.code, e.reason, file) except socket.timeout: errfunc(10054, "Socket timeout.\n", url) class DownloadFromWeb(AptOfflineLib.ProgressBar, GenericDownloadFunction): '''Class for DownloadFromWeb This class also inherits progressbar functionalities from parent class, ProgressBar''' def __init__(self, width, total_items): '''width = Progress Bar width''' AptOfflineLib.ProgressBar.__init__(self, width=width, total_items=total_items) def stripper(item): '''Strips extra characters from "item". Breaks "item" into: url - The URL file - The actual package file size - The file size checksum - The checksum string and returns them.''' item = item.split(' ') log.verbose("Item is %s\n" % (item) ) url = string.rstrip(string.lstrip(''.join(item[0]), chars="'"), chars="'") file = string.rstrip(string.lstrip(''.join(item[1]), chars="'"), chars="'") try: size = int(string.rstrip(string.lstrip(''.join(item[2]), chars = "'"), chars="'")) except ValueError: log.verbose("%s is malformed\n" % (" ".join(item) ) ) size = 0 #INFO: md5 ends up having '\n' with it. # That needs to be stripped too. try: checksum = string.rstrip(string.lstrip(''.join(item[3]), chars = "'"), chars = "'") checksum = string.rstrip(checksum, chars = "\n") except IndexError: if item[1].endswith("_Release") or item[1].endswith("_Release.gpg"): checksum = None return url, file, size, checksum def errfunc(errno, errormsg, filename): '''We use errfunc to handler errors. There are some error codes (-3 and 13 as of now) which are temporary codes, they happen when there is a temporary resolution failure, for example. For such situations, we can't abort because the uri file might have other hosts also, which might be well accessible. This function does the job of behaving accordingly as per the error codes.''' error_codes = [-3, 13, 504, 404, 401, 10060, 104, 101010] # 104, 'Connection reset by peer' # 504 is for gateway timeout # 404 is for URL error. Page not found. # 401 is for Restricted pages # 10060 is for Operation Time out. There can be multiple reasons for this timeout # 101010 is for socket max retry count # 10054 is for Socket Timeout. Socket Timeout are seen during network congestion #TODO: Find out what these error codes are for # and better document them the next time you find it out. # 13 is for "Permission Denied" when you don't have privileges to access the destination if errno in error_codes: log.err("%s - %s - %s.%s\n" % (filename, errno, errormsg, LINE_OVERWRITE_MID)) log.verbose("Will still try with other package uris\n") pass elif errno == 10054: log.verbose("%s - %s - %s.%s\n" % (filename, errno, errormsg, LINE_OVERWRITE_SMALL) ) pass elif errno == 407 or errno == 2: # These, I believe are from OSError/IOError exception. # I'll document it as soon as I confirm it. log.err("%s\n" % (errormsg)) sys.exit(errno) elif errno == 1: log.err(errormsg) log.err("Explicit program termination %s\n" % (errno)) sys.exit(errno) else: log.err("I don't understand this error code %s\nPlease file a bug report" % (errno)) def fetcher( args ): # get opts Str_GetArg = args.get Int_SocketTimeout = args.socket_timeout Str_DownloadDir = args.download_dir Str_CacheDir = args.cache_dir Bool_DisableMD5Check = args.disable_md5check Int_NumOfThreads = args.num_of_threads Str_BundleFile = args.bundle_file Str_ProxyHost = args.proxy_host Str_ProxyPort = args.proxy_port #Bool_GetUpdate = args.get_update #Bool_GetUpgrade = args.get_upgrade Bool_BugReports = args.deb_bugs global guiTerminateSignal if Int_SocketTimeout: try: Int_SocketTimeout.__int__() socket.setdefaulttimeout( Int_SocketTimeout ) log.verbose( "Default timeout now is: %d.\n" % ( socket.getdefaulttimeout() ) ) except AttributeError: log.err( "Incorrect value set for socket timeout.\n" ) sys.exit( 1 ) if Str_ProxyHost: if Str_ProxyPort: try: log.verbose(Str_ProxyHost + ":" + Str_ProxyPort) proxy_support = urllib2.ProxyHandler({'http': Str_ProxyHost + ":" + str(Str_ProxyPort) }) opener = urllib2.build_opener(proxy_support) urllib2.install_opener(opener) except : log.err("Handle this exception.\n") sys.exit(1) else: try: proxy_support = urllib2.ProxyHandler({'http': Str_ProxyHost}) opener = urllib2.build_opener(proxy_support) urllib2.install_opener(opener) except: log.err("Handle this exception.\n") sys.exit(1) #INFO: Python 2.5 has hashlib which supports sha256 # If we don't have Python 2.5, disable MD5/SHA256 checksum if AptOfflineLib.Python_2_5 is False: Bool_DisableMD5Check = True log.verbose( "\nMD5/SHA256 Checksum is being disabled. You need atleast Python 2.5 to do checksum verification.\n" ) if Str_GetArg: if os.path.isfile(Str_GetArg): log.msg( "\nFetching APT Data\n\n" ) else: log.err( "File not present. Check path.\n" ) sys.exit( 1 ) if Str_CacheDir is not None: if os.path.isdir( Str_CacheDir ) is False: log.verbose( "WARNING: cache dir %s is incorrect. Did you give the full path ?\n" % (Str_CacheDir) ) class FetcherClass( DownloadFromWeb, AptOfflineLib.Archiver, AptOfflineLib.Checksum ): def __init__( self, width, lock, total_items ): DownloadFromWeb.__init__( self, width=width, total_items=total_items ) #ProgressBar.__init__(self, width) #self.width = width AptOfflineLib.Archiver.__init__( self, lock=lock ) #self.lock = lock if Str_DownloadDir is None: tempdir = tempfile.gettempdir() if os.access( tempdir, os.W_OK ) is True: pidname = os.getpid() randomjunk = ''.join(chr(random.randint(97,122)) for x in xrange(5)) if guiBool else '' # 5 byte random junk to make mkdir possible multiple times # use-case -> download many sigs of different machines using one instance tempdir = os.path.join(tempdir , "apt-offline-downloads-" + str(pidname) + randomjunk) os.mkdir(tempdir) Str_DownloadDir = os.path.abspath(tempdir) else: log.err( "%s is not writable\n" % (tempdir) ) errfunc ( 1, '') else: if os.access( Str_DownloadDir, os.W_OK ) is True: Str_DownloadDir = os.path.abspath( Str_DownloadDir ) else: #INFO: If the path is provided, but doesn't exist # Create it. try: os.umask( 0002 ) os.mkdir( Str_DownloadDir ) Str_DownloadDir = os.path.abspath( Str_DownloadDir ) except: log.err( "I couldn't create directory %s\n" % (Str_DownloadDir) ) errfunc( 1, '' ) if Str_BundleFile is not None: Str_BundleFile = os.path.abspath(Str_BundleFile) if os.access(Str_BundleFile, os.F_OK ): log.err( "%s already present.\nRemove it first.\n" % ( Str_BundleFile ) ) sys.exit( 1 ) else: try: f = open(Str_BundleFile, 'w') except IOError: log.err("Cannot write to file %s\n" % (Str_BundleFile) ) sys.exit(1) if Bool_BugReports: if DebianBTS is True: if Str_BundleFile is not None: #INFO: We are creating an archive then. # For now, we support zip archives FetchBugReportsDebian = FetchBugReports( apt_bug_file_format, IgnoredBugTypes, Str_BundleFile, lock=True ) else: #INFO: No bundle file to be created. # Data will be stored in the Str_DownloadDir folder FetchBugReportsDebian = FetchBugReports( apt_bug_file_format, IgnoredBugTypes ) else: log.err( "Couldn't find debianbts module.\n Cannot fetch Bug Reports.\n" ) FetchData = {} #Info: Initialize an empty dictionary. #INFO: We don't distinguish in between what to fetch # We just rely on what a signature file lists us to get # It can be just debs or just package updates or both if Str_GetArg is not None: try: raw_data_list = open( Str_GetArg, 'r' ).readlines() except IOError, ( errno, strerror ): log.err( "%s %s\n" % ( errno, strerror ) ) errfunc( errno, '' ) FetchData['Item'] = [] for item in raw_data_list: # Interim fix for Debian bug #664654 (ItemURL, ItemFile, ItemSize, ItemChecksum) = stripper(item) if ItemURL.endswith("InRelease"): log.verbose("APT uses new InRelease auth mechanism") ExtraItemURL = ItemURL.rstrip(ItemURL.split("/")[-1]) GPGItemURL = "'" + ExtraItemURL + "Release.gpg" ReleaseItemURL = "'" + ExtraItemURL + "Release" ExtraItemFile = ItemFile.rstrip(ItemFile.split("_")[-1]) GPGItemFile = ExtraItemFile + "Release.gpg" ReleaseItemFile = ExtraItemFile + "Release" FetchData['Item'].append(GPGItemURL + " " + GPGItemFile + " " + str(ItemSize) + " " + ItemChecksum) log.verbose("Printing GPG URL/Files") log.verbose("%s %s" % (GPGItemURL, GPGItemFile) ) FetchData['Item'].append(ReleaseItemURL + " " + ReleaseItemFile + " " + str(ItemSize) + " " + ItemChecksum) log.verbose("Printing Release URL/Files") log.verbose("%s %s" % (ReleaseItemURL, ReleaseItemFile) ) FetchData['Item'].append( item ) del raw_data_list # INFO: Let's get the total number of items. This will get the # correct total count in the progress bar. total_items = len(FetchData['Item']) FetcherInstance = FetcherClass( width=30, lock=True, total_items=total_items ) #INFO: Thread Support if Int_NumOfThreads > 2: log.msg("WARNING: If you are on a slow connection, it is good to\n") log.msg("WARNING: limit the number of threads to a low number like 2.\n") log.msg("WARNING: Else higher number of threads executed could cause\n") log.msg("WARNING: network congestion and timeouts.\n\n") def DataFetcher(request, response, func=find_first_match): '''Get items from the request Queue, process them with func(), put the results along with the Thread's name into the response Queue. Stop running when item is None.''' #while 1: #tuple_item_key = request.get() #if tuple_item_key is None: # break #(key, item) = tuple_item_key (key, item) = request # On many boxes, the cdrom apt repository will be enabled. # For now, let's skip the cdrom repository items. if item.startswith("\'cdrom"): log.verbose("cdrom apt repository not supported. Skipping\n") log.verbose(item) return True #INFO: Everything (url, file, download_size, checksum) = stripper(item) thread_name = threading.currentThread().getName() log.verbose("Thread is %s\n" % (thread_name) ) if url.endswith(".deb"): try: PackageName = file.split("_")[0] except IndexError: log.err("Not getting a package name here is problematic. Better bail out.\n") sys.exit(1) #INFO: For Package version, we don't want to fail try: PackageVersion = file.split("_")[1] except IndexError: PackageVersion = "NA" log.verbose("Weird!! Package version not present. Is it really a deb file?\n") #INFO: find_first_match() returns False or a file name with absolute path full_file_path = func(Str_CacheDir, file) #INFO: If we find the file in the local Str_CacheDir, we'll execute this block. if full_file_path != False: # We'll first check for its md5 checksum if Bool_DisableMD5Check is False: if FetcherInstance.CheckHashDigest(full_file_path, checksum) is True: log.verbose("Checksum correct for package %s.%s\n" % (PackageName, LINE_OVERWRITE_FULL) ) bug_fetched = False if Bool_BugReports: log.verbose("Fetching bug reports for package %s.%s\n" % (PackageName, LINE_OVERWRITE_FULL) ) if FetchBugReportsDebian.FetchBugsDebian(PackageName) in [1,2]: log.verbose("Fetched bug reports for package %s.%s\n" % (PackageName, LINE_OVERWRITE_FULL) ) bug_fetched = True else: log.verbose("Couldn't fetch bug reports for package %s.%s\n" % (PackageName, LINE_OVERWRITE_MID) ) if Str_BundleFile: if FetcherInstance.compress_the_file(Str_BundleFile, full_file_path) is True: log.success("%s copied from local cache directory %s.%s\n" % (PackageName, Str_CacheDir, LINE_OVERWRITE_MID) ) else: log.err("Couldn't add %s to archive %s.%s\n" % (file, Str_BundleFile, LINE_OVERWRITE_MID) ) sys.exit(1) #INFO: If no zip option enabled, simply copy the downloaded package file # along with the downloaded bug reports. else: try: shutil.copy(full_file_path, Str_DownloadDir) log.success("%s copied from local cache directory %s.%s\n" % (PackageName, Str_CacheDir, LINE_OVERWRITE_MID) ) except shutil.Error: log.verbose("%s already available in %s. Skipping copy!!!%s\n" % (file, Str_DownloadDir, LINE_OVERWRITE_MID) ) if bug_fetched is True: for x in os.listdir(os.curdir): if (x.startswith(PackageName) and x.endswith(apt_bug_file_format) ): try: shutil.move(x, Str_DownloadDir) except: #INFO: This should fix DBTS #584427 log.verbose("Exception thrown. Most likely it is because the cache_dir and download_dir locations are the same.\n") log.verbose("Moved %s file to %s folder.%s\n" % (x, Str_DownloadDir, LINE_OVERWRITE_FULL) ) #INFO: Damn!! The md5chesum didn't match :-( # The file is corrupted and we need to download a new copy from the internet else: log.verbose("%s checksum mismatch. Skipping file.%s\n" % (file, LINE_OVERWRITE_FULL) ) log.msg("Downloading %s - %s %s\n" % (PackageName, log.calcSize(download_size/1024), LINE_OVERWRITE_MID) ) if FetcherInstance.download_from_web(url, file, Str_DownloadDir) == True: log.success("\r%s done.%s\n" % (PackageName, LINE_OVERWRITE_FULL) ) #Add to Str_CacheDir if possible if Str_CacheDir and os.access(Str_CacheDir, os.W_OK) == True: try: shutil.copy(file, Str_CacheDir) log.verbose("%s copied to local cache directory %s.%s\n" % (file, Str_CacheDir, LINE_OVERWRITE_MID) ) except shutil.Error: log.verbose("Couldn't copy %s to %s.%s\n" % (file, Str_CacheDir, LINE_OVERWRITE_FULL) ) else: log.verbose("cache_dir %s is not writeable. Skipping copy to it.\n" % (Str_CacheDir) ) #Fetch bug reports if Bool_BugReports: log.verbose("Fetching bug reports for package %s.%s\n" % (PackageName, LINE_OVERWRITE_MID) ) if FetchBugReportsDebian.FetchBugsDebian( PackageName ) in [1, 2]: log.verbose( "Fetched bug reports for package %s.%s\n" % ( PackageName, LINE_OVERWRITE_MID ) ) else: log.verbose( "Couldn't fetch bug reports for package %s.%s\n" % ( PackageName, LINE_OVERWRITE_MID ) ) if Str_BundleFile: if FetcherInstance.compress_the_file( Str_BundleFile, file ) != True: log.err( "Couldn't archive %s to file %s.%s\n" % ( file, Str_BundleFile, LINE_OVERWRITE_SMALL ) ) sys.exit( 1 ) else: log.verbose( "%s added to archive %s.%s\n" % ( file, Str_BundleFile, LINE_OVERWRITE_SMALL ) ) os.unlink( os.path.join( Str_DownloadDir, file ) ) #INFO: You're and idiot. # You should NOT disable md5checksum for any files else: bug_fetched = False if Bool_BugReports: log.verbose("Fetching bug reports for package %s.%s\n" % (PackageName, LINE_OVERWRITE_MID) ) if FetchBugReportsDebian.FetchBugsDebian( PackageName ) in [1, 2]: log.verbose( "Fetched bug reports for package %s.%s\n" % ( PackageName, LINE_OVERWRITE_MID ) ) bug_fetched = True else: log.verbose( "Couldn't fetch bug reports for package %s.%s\n" % ( PackageName, LINE_OVERWRITE_MID ) ) #FIXME: Don't know why this was really required. If this has no changes, delete it. #file = full_file_path.split("/") #file = file[len(file) - 1] #file = download_path + "/" + file if Str_BundleFile: if FetcherInstance.compress_the_file( Str_BundleFile, file ) != True: log.err( "Couldn't archive %s to file %s.%s\n" % ( file, Str_BundleFile, LINE_OVERWRITE_SMALL ) ) sys.exit( 1 ) else: log.verbose( "%s added to archive %s.%s\n" % ( file, Str_BundleFile, LINE_OVERWRITE_SMALL ) ) os.unlink( os.path.join( Str_DownloadDir, file ) ) else: # Since zip file option is not enabled let's copy the file to the target folder try: shutil.copy( full_file_path, Str_DownloadDir ) log.success( "%s copied from local cache directory %s.%s\n" % ( file, Str_CacheDir, LINE_OVERWRITE_SMALL ) ) except shutil.Error: log.verbose( "%s already available in dest_dir. Skipping copy!!!%s\n" % ( file, LINE_OVERWRITE_SMALL ) ) # And also the bug reports if bug_fetched is True: for x in os.listdir( os.curdir ): if ( x.startswith( PackageName ) and x.endswith( apt_bug_file_format ) ): shutil.move( x, Str_DownloadDir ) log.verbose( "Moved %s file to %s folder.%s\n" % ( x, Str_DownloadDir, LINE_OVERWRITE_MID ) ) else: #INFO: This block gets executed if the file is not found in local Str_CacheDir or Str_CacheDir is None # We go ahead and try to download it from the internet log.verbose( "%s not available in local cache %s.%s\n" % ( file, Str_CacheDir, LINE_OVERWRITE_MID ) ) log.msg( "Downloading %s %s - %s %s\n" % ( PackageName, PackageVersion, log.calcSize( download_size / 1024 ), LINE_OVERWRITE_MID ) ) if FetcherInstance.download_from_web( url, file, Str_DownloadDir ) == True: #INFO: This block gets executed if md5checksum is allowed if Bool_DisableMD5Check is False: #INFO: Debian moved to SHA256. So we use that now. Older systems could have md5 log.verbose( "File %s has checksum %s\n" % ( file, checksum ) ) if FetcherInstance.CheckHashDigest( file, checksum ) is True: if Str_CacheDir and os.access( Str_CacheDir, os.W_OK ) == True: try: shutil.copy( file, Str_CacheDir ) log.verbose( "%s copied to local cache directory %s.%s\n" % ( file, Str_CacheDir, LINE_OVERWRITE_MID ) ) except shutil.Error: log.verbose( "%s already available in %s. Skipping copy!!!%s\n" % ( file, Str_CacheDir, LINE_OVERWRITE_MID ) ) else: log.verbose( "Str_CacheDir %s is not writeable. Skipping copy to it.\n" % ( Str_CacheDir ) ) if Bool_BugReports: log.verbose("Fetching bug reports for package %s.%s\n" % (PackageName, LINE_OVERWRITE_MID) ) if FetchBugReportsDebian.FetchBugsDebian( PackageName ) in [1, 2]: log.verbose( "Fetched bug reports for package %s.%s\n" % ( PackageName, LINE_OVERWRITE_MID ) ) else: log.verbose( "Couldn't fetch bug reports for package %s.%s\n" % ( PackageName, LINE_OVERWRITE_MID ) ) if Str_BundleFile: if FetcherInstance.compress_the_file( Str_BundleFile, file ) != True: log.err( "Couldn't archive %s to file %s.%s\n" % ( file, Str_BundleFile, LINE_OVERWRITE_SMALL ) ) sys.exit( 1 ) else: log.verbose( "%s added to archive %s.%s\n" % ( file, Str_BundleFile, LINE_OVERWRITE_SMALL ) ) os.unlink( os.path.join( Str_DownloadDir, file ) ) log.success( "\r%s %s done.%s\n" % ( PackageName, PackageVersion, LINE_OVERWRITE_FULL ) ) else: #INFO MD5 Checksum is incorrect. log.err( "%s Checksum mismatch.\n" % ( PackageName ) ) errlist.append( PackageName ) else: if Bool_BugReports: log.verbose("Fetching bug reports for package %s.%s\n" % (PackageName, LINE_OVERWRITE_MID) ) if FetchBugReportsDebian.FetchBugsDebian( PackageName ) in [1, 2]: log.verbose( "Fetched bug reports for package %s.%s\n" % ( PackageName, LINE_OVERWRITE_MID ) ) else: log.verbose( "Couldn't fetch bug reports for package %s.%s\n" % ( PackageName, LINE_OVERWRITE_MID ) ) if Str_BundleFile: if FetcherInstance.compress_the_file( Str_BundleFile, file ) != True: log.err( "Couldn't archive %s to file %s.%s\n" % ( file, Str_BundleFile, LINE_OVERWRITE_SMALL ) ) sys.exit( 1 ) else: log.verbose( "%s added to archive %s.%s\n" % ( file, Str_BundleFile, LINE_OVERWRITE_SMALL ) ) os.unlink( os.path.join( Str_DownloadDir, file ) ) log.success( "\r%s %s done.%s\n" % ( PackageName, PackageVersion, LINE_OVERWRITE_FULL ) ) else: errlist.append( PackageName ) else: def DownloadPackages(url): if FetcherInstance.download_from_web(url, file, Str_DownloadDir) == True: log.success("\r%s done.%s\n" % (url, LINE_OVERWRITE_FULL) ) if Str_BundleFile: if FetcherInstance.compress_the_file(Str_BundleFile, file) != True: log.err("Couldn't archive %s to file %s.%s\n" % (file, Str_BundleFile, LINE_OVERWRITE_MID) ) sys.exit(1) else: log.verbose("%s added to archive %s.%s\n" % (file, Str_BundleFile, LINE_OVERWRITE_FULL) ) os.unlink(os.path.join(Str_DownloadDir, file) ) return True else: return False #INFO: Handle the multiple Packages formats. # See DTBS #583502 SupportedFormats = ["bz2", "gz", "lzma"] #INFO: We are a package update PackageName = url PackageFile = url.split("/")[-1] PackageFormat = PackageFile.split(".")[-1] if PackageFormat in SupportedFormats: SupportedFormats.remove(PackageFormat) #Remove the already tried format log.msg("Downloading %s.%s\n" % (PackageName, LINE_OVERWRITE_MID) ) if DownloadPackages(url) is False and guiTerminateSignal is False: # dont proceed retry if Ctrl+C in cli log.verbose("%s failed. Retry with the remaining possible formats" % (url) ) # We could fail with the Packages format of what apt gave us. We can try the rest of the formats that apt or the archive could support for Format in SupportedFormats: NewPackageFile = PackageFile.split(".")[0] + "." + Format NewUrl = url.strip(url.split("/")[-1]) + NewPackageFile log.verbose("Retry download %s.%s\n" % (NewUrl, LINE_OVERWRITE_MID) ) if DownloadPackages(NewUrl) is True: break else: errlist.append(NewUrl) # Create two Queues for the requests and responses requestQueue = Queue.Queue() responseQueue = Queue.Queue() # create size metadata for progress for key in FetchData.keys(): for item in FetchData.get(key): if guiBool: #REAL_PROGRESS: to calculate the total download size, NOTE: initially this was under the loop that Queued the items if guiTerminateSignal: break try: (url, file, download_size, checksum) = stripper(item) size = int(download_size) if size == 0: log.msg("MSG_START") temp = urllib2.urlopen(url) headers = temp.info() size = int(headers['Content-Length']) totalSize[0] += size except: ''' some int parsing problem ''' if not guiTerminateSignal: ConnectThread = AptOfflineLib.MyThread(DataFetcher, requestQueue, responseQueue, Int_NumOfThreads) ConnectThread.startThreads() # Queue up the requests. #for item in raw_data_list: requestQueue.put(item) for key in FetchData.keys(): for item in FetchData.get(key): ConnectThread.populateQueue( (key, item) ) if guiBool: log.msg("MSG_END") guiMetaCompleted=True # For the sake of a responsive GUI while (ConnectThread.threads_finished < ConnectThread.threads): # handle signals from gui here if guiTerminateSignal: # stop all ongoing work #TODO: find a way to stop those threads here ConnectThread.guiTerminateSignal=True for thread in ConnectThread.thread_pool: thread.guiTerminateSignal=True ConnectThread.stopThreads() ConnectThread.stopQueue(timeout=0.2) return ConnectThread.stopThreads() ConnectThread.stopQueue(timeout=0.2) # let them work for 0.2s log.msg ("[%d/%d]" %(totalSize[1], totalSize[0])) else: # else go by the normal CLI way while ConnectThread.threads_finished < ConnectThread.threads: try: ConnectThread.stopThreads() ConnectThread.stopQueue(0.2) except KeyboardInterrupt: # user pressed Ctrl-c, signal all threads to exit guiTerminateSignal=True # this would signal download_from_web to stop ConnectThread.guiTerminateSignal=True for thread in ConnectThread.thread_pool: thread.guiTerminateSignal=True # tell all threads to exit ConnectThread.stopThreads() ConnectThread.stopQueue() log.err("\nInterrupted by user. Exiting!\n") sys.exit(0) # Print the failed files if len(errlist) > 0: log.msg("\n\n") log.err("The following files failed to be downloaded.\n") for error in errlist: log.err("%s failed.\n" % (error)) if args.bundle_file: log.msg("\nDownloaded data to %s\n" % (Str_BundleFile) ) else: log.msg("\nDownloaded data to %s\n" % (Str_DownloadDir) ) def installer( args ): class APTVerifySigs: def __init__(self, gpgv=None, keyring=None): if gpgv is None: self.gpgv="/usr/bin/gpgv" else: self.gpgv=gpgv if keyring is None: self.opts="--keyring /etc/apt/trusted.gpg --ignore-time-conflict" else: self.opts = "--keyring %s --ignore-time-conflict" % (keyring) def VerifySig(self, signature_file, signed_file): if not os.access(signature_file, os.F_OK): log.err("%s is bad. Can't proceed.\n" % (signature_file) ) return False if not os.access(signed_file, os.F_OK): log.err("%s is bad. Can't proceed.\n" % (signed_file) ) return False x = os.system("%s %s %s %s" % (self.gpgv, self.opts, signature_file, signed_file) ) #TODO: Find a way to redirect std[out|err] # look at subprocess module if x == 0: return True else: return False # install opts Str_InstallArg = args.install Bool_TestWindows = args.simulate Bool_SkipBugReports = args.skip_bug_reports Bool_Untrusted = args.allow_unauthenticated Str_InstallSrcPath = args.install_src_path # Old cruft. Needs clean-up install_file_path = Str_InstallArg if Str_InstallSrcPath is None: tempdir = tempfile.gettempdir() if os.access( tempdir, os.W_OK ) is True: pidname = os.getpid() randomjunk = ''.join(chr(random.randint(97,122)) for x in xrange(5)) if guiBool else '' # 5 byte random junk to make mkdir possible multiple times # use-case -> installing multiple bundles with one dialog tempdir = os.path.join(tempdir , "apt-offline-src-downloads-" + str(pidname) + randomjunk ) os.mkdir(tempdir) Str_InstallSrcPath = os.path.abspath(tempdir) else: log.err( "%s is not writable\n" % (tempdir) ) sys.exit(1) if not os.path.isdir(Str_InstallSrcPath): log.err("Not a folder.\n") sys.exit(1) if os.access(Str_InstallSrcPath, os.W_OK) is not True: log.err("%s is not writable.\n" % (Str_InstallSrcPath)) sys.exit(1) if Str_InstallArg: if Bool_TestWindows: global apt_package_target_path tempdir = tempfile.gettempdir() if os.access( tempdir, os.W_OK ) is True: pidname = os.getpid() tempdir = os.path.join(tempdir , "apt-package-target-path-" + str(pidname) ) log.verbose("apt-package-target-path is %s\n" % (tempdir) ) os.mkdir(tempdir) apt_package_target_path = os.path.abspath(tempdir) else: log.err( "%s is not writable\n" % (tempdir) ) sys.exit(1) global apt_update_target_path tempdir = tempfile.gettempdir() if os.access( tempdir, os.W_OK ) is True: pidname = os.getpid() tempdir = os.path.join(tempdir , "apt-update-target-path-" + str(pidname) ) log.verbose("apt-update-target-path is %s\n" % (tempdir) ) os.mkdir(tempdir) apt_update_target_path = os.path.abspath(tempdir) else: log.err( "%s is not writable\n" % (tempdir) ) sys.exit(1) global apt_update_final_path tempdir = tempfile.gettempdir() if os.access( tempdir, os.W_OK ) is True: pidname = os.getpid() tempdir = os.path.join(tempdir , "apt-update-final-path-" + str(pidname) ) log.verbose("apt-update-final-path is %s\n" % (tempdir) ) os.mkdir(tempdir) apt_update_final_path = os.path.abspath(tempdir) else: log.err( "%s is not writable\n" % (tempdir) ) sys.exit(1) else: try: if os.geteuid() != 0: log.err("You need superuser privileges to execute this option\n") sys.exit(1) except AttributeError: log.err("Are you really running the install command on a Debian box?\n") sys.exit(1) archive = AptOfflineLib.Archiver() archive_file_types = ['application/x-bzip2', 'application/gzip', 'application/zip'] if not Bool_Untrusted: AptSecure = APTVerifySigs() try: #INFO: Let's clean the partial database for x in os.listdir(apt_update_target_path): x = os.path.join(apt_update_target_path, x) if os.access(x, os.W_OK): os.unlink(x) log.verbose("Cleaning old update data file %s.\n" % (x) ) except OSError: log.err("Cannot find APT's partial cache dir %s\n" % (apt_update_target_path) ) sys.exit(1) def display_options(): log.msg( "(Y) Yes. Proceed with installation\n" ) log.msg( "(N) No, Abort.\n" ) log.msg( "(R) Redisplay the list of bugs.\n" ) log.msg( "(Bug Number) Display the bug report from the Offline Bug Reports.\n" ) log.msg( "(?) Display this help message.\n" ) def get_response(): response = raw_input( "What would you like to do next:\t (y, N, Bug Number, R, ?)" ) response = response.rstrip( "\r" ) return response def list_bugs(dictList): ''' Takes a dictionary of key,value pair where: key => filename value => subject string ''' log.msg( "\n\nFollowing are the list of bugs present.\n" ) sortedKeyList = dictList.keys() sortedKeyList.sort() for each_bug in sortedKeyList: pkg_name = each_bug.split( '.' )[-3].split('/')[-1] bug_num = each_bug.split( '.' )[-2] bug_subject = dictList[each_bug] log.msg( "%s\t%s\t%s\n" % ( pkg_name, bug_num, bug_subject ) ) def magic_check_and_uncompress( archive_file=None, filename=None): retval = False if AptOfflineMagicLib.file( archive_file ) == "application/x-bzip2" or \ AptOfflineMagicLib.file( archive_file ) == "application/x-gzip": temp_filename = os.path.join(apt_update_target_path, filename + app_name) filename = os.path.join(apt_update_target_path, filename) if AptOfflineMagicLib.file( archive_file ) == "application/x-bzip2": retval = archive.decompress_the_file( archive_file, temp_filename, "bzip2" ) elif AptOfflineMagicLib.file( archive_file ) == "application/x-gzip": retval = archive.decompress_the_file( archive_file, temp_filename, "gzip" ) else: retval = False if retval is True: os.rename(temp_filename, filename) else: os.unlink(temp_filename) elif AptOfflineMagicLib.file( archive_file ) == "application/zip": retval = archive.decompress_the_file( os.path.join( install_file_path, eachfile ), apt_update_target_path, eachfile, "zip" ) elif AptOfflineMagicLib.file( archive_file ) == "PGP armored data": filename = os.path.join(apt_update_target_path, filename) shutil.copy2(archive_file, filename) # PGP armored data should be bypassed log.verbose("File is %s, hence 'True'.\n" % (filename) ) retval = True elif AptOfflineMagicLib.file( archive_file ) == "application/x-dpkg": filename = os.path.join(apt_package_target_path, filename) if os.access( apt_package_target_path, os.W_OK ): shutil.copy2( archive_file, filename ) log.msg("%s file synced.\n" % (filename) ) retval = True else: log.err( "Cannot write to target path %s\n" % ( apt_package_target_path ) ) sys.exit( 1 ) elif filename.endswith( apt_bug_file_format ): pass elif AptOfflineMagicLib.file( archive_file ) == "ASCII text": filename = os.path.join(apt_update_target_path, filename) if os.access( apt_update_target_path, os.W_OK ): shutil.copy( archive_file, filename ) retval = True else: log.err( "Cannot write to target path %s\n" % ( apt_update_target_path ) ) sys.exit( 1 ) else: log.err( "I couldn't understand file type %s.\n" % ( filename ) ) if retval: #CHANGE: track progress totalSize[0]+=1 if guiBool: log.msg("[%d/%d]" % (totalSize[0], totalSize[1])) #ENDCHANGE log.verbose( "%s file synced to %s.\n" % ( filename, apt_update_target_path ) ) if os.path.isfile(install_file_path): #INFO: For now, we support zip bundles only file = zipfile.ZipFile( install_file_path, "r" ) #CHANGE: for progress tracking totalSize[1] = len(file.namelist()) totalSize[0] = 0 #ENDCHANGE SrcPkgDict = {} for filename in file.namelist(): if filename.endswith(".dsc"): SrcPkgName = filename.split('_')[0] temp = tempfile.NamedTemporaryFile() temp.file.write( file.read( filename ) ) temp.file.flush() temp.file.seek( 0 ) #Let's go back to the start of the file SrcPkgDict[SrcPkgName] = [] for SrcPkgIdentifier in temp.file.readlines(): if SrcPkgIdentifier.startswith(' ') and not SrcPkgIdentifier.isspace(): SrcPkgIdentifier = SrcPkgIdentifier.split(' ')[3].rstrip("\n") if SrcPkgIdentifier in SrcPkgDict[SrcPkgName]: break else: SrcPkgDict[SrcPkgName].append(SrcPkgIdentifier) SrcPkgDict[SrcPkgName].append(filename) temp.file.close() #if bug_parse_required is True: bugs_number = {} if Bool_SkipBugReports: log.verbose("Skipping bug report check as requested") else: for filename in file.namelist(): if filename.endswith( apt_bug_file_format ): temp = tempfile.NamedTemporaryFile() temp.file.write( file.read( filename ) ) temp.file.flush() temp.file.seek( 0 ) #Let's go back to the start of the file for bug_subject_identifier in temp.file.readlines(): if bug_subject_identifier.startswith( '#' ): subject = bug_subject_identifier.lstrip( bug_subject_identifier.split( ":" )[0] ) subject = subject.rstrip( "\n" ) break bugs_number[filename] = subject temp.file.close() log.verbose(str(bugs_number) + "\n") if bugs_number: # Display the list of bugs list_bugs(bugs_number) display_options() response = get_response() while True: if response == "?": display_options() response = get_response() elif response.startswith( 'y' ) or response.startswith( 'Y' ): for filename in file.namelist(): #INFO: Take care of Src Pkgs found = False for item in SrcPkgDict.keys(): if filename in SrcPkgDict[item]: found = True break data = tempfile.NamedTemporaryFile() data.file.write( file.read( filename ) ) data.file.flush() archive_file = data.name if found is True: shutil.copy2(archive_file, os.path.join(Str_InstallSrcPath, filename) ) log.msg("Installing src package file %s to %s.\n" % (filename, Str_InstallSrcPath) ) continue magic_check_and_uncompress( archive_file, filename ) data.file.close() sys.exit( 0 ) elif response.startswith( 'n' ) or response.startswith( 'N' ): log.err( "Exiting gracefully on user request.\n\n" ) sys.exit( 0 ) elif response.isdigit() is True: found = False for full_bug_file_name in bugs_number: if response in full_bug_file_name: bug_file_to_display = full_bug_file_name found = True break if found == False: log.err( "Incorrect bug number %s provided.\n" % ( response ) ) response = get_response() if found: pydoc.pager(file.read(bug_file_to_display) ) # Redisplay the menu # FIXME: See a pythonic possibility of cleaning the screen at this stage response = get_response() elif response.startswith( 'r' ) or response.startswith( 'R' ): list_bugs(bugs_number) response = get_response() else: log.err( 'Incorrect choice. Exiting\n' ) sys.exit( 1 ) else: log.verbose( "Great!!! No bugs found for all the packages that were downloaded.\n\n" ) #response = raw_input( "Continue with Installation. Y/N ?" ) #response = response.rstrip( "\r" ) #if response.endswith( 'y' ) or response.endswith( 'Y' ): # log.verbose( "Continuing with syncing the files.\n" ) for filename in file.namelist(): #INFO: Take care of Src Pkgs found = False for item in SrcPkgDict.keys(): if filename in SrcPkgDict[item]: found = True break data = tempfile.NamedTemporaryFile() data.file.write( file.read( filename ) ) data.file.flush() archive_file = data.name if found is True: shutil.copy2(archive_file, os.path.join(Str_InstallSrcPath, filename) ) log.msg("Installing src package file %s to %s.\n" % (filename, Str_InstallSrcPath) ) continue magic_check_and_uncompress( archive_file, filename ) data.file.close() #else: # log.msg( "Exiting gracefully on user request.\n" ) # sys.exit( 0 ) elif os.path.isdir(install_file_path): SrcPkgDict = {} for filename in os.listdir( install_file_path ): if filename.endswith(".dsc"): SrcPkgName = filename.split('_')[0] SrcPkgDict[SrcPkgName] = [] Tempfile = os.path.join(install_file_path, filename) temp = open(Tempfile, 'r') for SrcPkgIdentifier in temp.readlines(): if SrcPkgIdentifier.startswith(' ') and not SrcPkgIdentifier.isspace(): SrcPkgIdentifier = SrcPkgIdentifier.split(' ')[3].rstrip("\n") if SrcPkgIdentifier in SrcPkgDict[SrcPkgName]: break else: SrcPkgDict[SrcPkgName].append(SrcPkgIdentifier) SrcPkgDict[SrcPkgName].append(filename) temp.close() bugs_number = {} def DirInstallPackages(InstallDirPath): for eachfile in os.listdir( InstallDirPath ): filename = eachfile FullFileName = os.path.abspath(os.path.join(InstallDirPath, eachfile) ) if os.path.isdir(FullFileName): log.verbose("Skipping!! %s is a directory\n" % (FullFileName)) continue #INFO: Take care of Src Pkgs found = False for item in SrcPkgDict.keys(): if filename in SrcPkgDict[item]: found = True break if found is True: shutil.copy2(FullFileName, Str_InstallSrcPath) log.msg("Installing src package file %s to %s.\n" % (filename, Str_InstallSrcPath) ) continue magic_check_and_uncompress( FullFileName, filename ) return True if Bool_SkipBugReports: log.verbose("Skipping bug report check as requested") else: for filename in os.listdir( install_file_path ): if filename.endswith( apt_bug_file_format ): filename = os.path.join(install_file_path, filename) temp = open(filename, 'r') for bug_subject_identifier in temp.readlines(): if bug_subject_identifier.startswith( '#' ): subject = bug_subject_identifier.lstrip( bug_subject_identifier.split( ":" )[0] ) subject = subject.rstrip( "\n" ) break bugs_number[filename] = subject temp.close() log.verbose(str(bugs_number) + "\n") if bugs_number: #Give the choice to the user list_bugs(bugs_number) display_options() response = get_response() while True: if response == "?": display_options() response = get_response() elif response.startswith( 'y' ) or response.startswith( 'Y' ): if DirInstallPackages(install_file_path) is True: sys.exit(0) else: log.err("Failed during install operation on %s.\n" % (install_file_path) ) sys.exit(1) elif response.startswith( 'n' ) or response.startswith( 'N' ): log.err( "Exiting gracefully on user request.\n\n" ) sys.exit( 0 ) elif response.isdigit() is True: found = False for full_bug_file_name in bugs_number: if response in full_bug_file_name: bug_file_to_display = full_bug_file_name found = True break if found == False: log.err( "Incorrect bug number %s provided.\n" % ( response ) ) response = get_response() if found: file = open(bug_file_to_display, 'r') pydoc.pager(file.read() ) # Redisplay the menu # FIXME: See a pythonic possibility of cleaning the screen at this stage response = get_response() elif response.startswith( 'r' ) or response.startswith( 'R' ): list_bugs(bugs_number) response = get_response() else: log.err( 'Incorrect choice. Exiting\n' ) sys.exit( 1 ) else: log.verbose( "Great!!! No bugs found for all the packages that were downloaded.\n\n" ) DirInstallPackages(install_file_path) if Bool_Untrusted: log.err("Disabling apt gpg check can risk your machine to compromise.\n") for x in os.listdir(apt_update_target_path): x = os.path.join(apt_update_target_path, x) shutil.copy2(x, apt_update_final_path) # Do we do a move ?? log.verbose("%s %s\n" % (x, apt_update_final_path) ) log.msg("%s synced.\n" % (x) ) else: lFileList= os.listdir(apt_update_target_path) lFileList.sort() lVerifiedWhitelist = [] for file in lFileList: file = os.path.join(apt_update_target_path, file) if file.endswith('.gpg'): log.verbose("%s\n" % (file) ) file = os.path.abspath(file) if AptSecure.VerifySig(file, file.rstrip(".gpg") ): file = file.rstrip("Release.gpg") file = file[:-1] #Remove the trailing _ underscore file = file.split("/")[-1] lVerifiedWhitelist.append(file) log.verbose("%s is gpg clean\n" % (file) ) else: # Bad sig. log.err("%s bad signature. Not syncing because in strict mode.\n" % (file) ) if lVerifiedWhitelist != []: log.verbose (str(lVerifiedWhitelist) + "\n") for whitelist_item in lVerifiedWhitelist: for final_item in lFileList: if whitelist_item in final_item: final_item = os.path.join(apt_update_target_path, final_item) shutil.copy2(final_item, apt_update_final_path) log.msg("%s synced.\n" % (final_item) ) def setter(args): #log.verbose(str(args)) # commented to keep setter UI sane for time Str_SetArg = args.set List_SetInstallPackages = args.set_install_packages List_SetInstallSrcPackages = args.set_install_src_packages Str_SetInstallRelease = args.set_install_release Bool_SetUpdate = args.set_update Bool_SetUpgrade = args.set_upgrade Str_SetUpgradeType = args.upgrade_type Bool_SrcBuildDep = args.src_build_dep Bool_TestWindows = args.simulate if Bool_SetUpdate is False and Bool_SetUpgrade is False and List_SetInstallPackages is None \ and List_SetInstallSrcPackages is None: Default_Operation = True else: Default_Operation = False #INFO: Don't run the default behavior, of SetUpdate and SetUpgrade, if the # user requests only for Package Installs if Default_Operation: Bool_SetUpdate = True Bool_SetUpgrade = True class AptManip: def __init__(self, OutputFile, Simulate=False, AptType="apt"): self.WriteTo = OutputFile self.Simulate = Simulate if AptType == "apt": self.apt = "apt-get" elif AptType == "aptitude": self.apt = "aptitude" elif AptType == "python-apt": #TODO: pass else: self.apt = "apt-get" def __Simulate(self): if self.Simulate is True: pass def __ExecSystemCmd(self, CommandString): if self.Simulate: return True else: if os.system( CommandString ) != 0: return False return True def Update(self): if self.apt == "apt-get": self.__AptGetUpdate() elif self.apt == "aptitude": pass else: log.err("Method not supported") sys.exit(1) def Upgrade(self, UpgradeType="upgrade", ReleaseType=None): if self.apt == "apt-get": self.__AptGetUpgrade(UpgradeType, ReleaseType) elif self.apt == "aptitude": pass else: log.err("Method not supported") sys.exit(1) def InstallPackages(self, PackageList, ReleaseType): if self.apt == "apt-get": self.__AptInstallPackage(PackageList, ReleaseType) else: log.err("Method not supported") sys.exit(1) def InstallSrcPackages(self, SrcPackageList, ReleaseType, BuildDependency): if self.apt == "apt-get": self.__AptInstallSrcPackages(SrcPackageList, ReleaseType, BuildDependency) else: log.err("Method not supported") sys.exit(1) def __FixAptSigs(self): for file in os.listdir(apt_update_target_path): if file.endswith(".gpg.reverify"): sig_file = file.rstrip(".reverify") log.verbose("Recovering gpg signature %s.\n" % (file) ) file = os.path.join(apt_update_target_path, file) os.rename(file, os.path.join(apt_update_final_path + sig_file) ) def __AptGetUpdate(self): log.msg("\nGenerating database of files that are needed for an update.\n") #FIXME: Unicode Fix # This is only a workaround. # When using locales, we get translation files. But apt doesn't extract the URI properly. # Once the extraction problem is root-caused, we can fix this easily. os.environ['__apt_set_update'] = self.WriteTo try: old_environ = os.environ['LANG'] except KeyError: old_environ = "C" os.environ['LANG'] = "C" log.verbose( "Set environment variable for LANG from %s to %s temporarily.\n" % ( old_environ, os.environ['LANG'] ) ) if self.__ExecSystemCmd('/usr/bin/apt-get -qq --print-uris --simulate update >> $__apt_set_update') is False: log.err( "FATAL: Something is wrong with the apt system.\n" ) log.verbose( "Set environment variable for LANG back to its original from %s to %s.\n" % ( os.environ['LANG'], old_environ ) ) os.environ['LANG'] = old_environ log.verbose("Calling __FixAptSigs to fix the apt sig problem") self.__FixAptSigs() def __AptitudeUpdate(self): pass def __PythonAptUpdate(self): pass def __AptGetUpgrade(self, UpgradeType="upgrade", ReleaseType=None): self.ReleaseType = ReleaseType os.environ['__apt_set_upgrade'] = self.WriteTo if ReleaseType is not None: os.environ['__apt_set_install_release'] = self.ReleaseType if UpgradeType == "upgrade": log.msg( "\nGenerating database of files that are needed for an upgrade.\n" ) if self.__ExecSystemCmd('/usr/bin/apt-get -qq --print-uris -t $__apt_set_install_release upgrade >> $__apt_set_upgrade') is False: log.err( "FATAL: Something is wrong with the apt system.\n" ) elif Str_SetUpgradeType == "dist-upgrade": log.msg( "\nGenerating database of files that are needed for a dist-upgrade.\n" ) if self.__ExecSystemCmd( '/usr/bin/apt-get -qq --print-uris -t $__apt_set_install_release dist-upgrade >> $__apt_set_upgrade' ) is False: log.err( "FATAL: Something is wrong with the apt system.\n" ) elif Str_SetUpgradeType == "dselect-upgrade": log.msg( "\nGenerating database of files that are needed for a dselect-upgrade.\n" ) if self.__ExecSystemCmd( '/usr/bin/apt-get -qq --print-uris -t $__apt_set_install_release dselect-upgrade >> $__apt_set_upgrade' ) is False: log.err( "FATAL: Something is wrong with the apt system.\n" ) else: log.err( "Invalid upgrade argument type selected\nPlease use one of, upgrade/dist-upgrade/dselect-upgrade\n" ) else: if UpgradeType == "upgrade": log.msg( "\nGenerating database of files that are needed for an upgrade.\n" ) if self.__ExecSystemCmd('/usr/bin/apt-get -qq --print-uris upgrade >> $__apt_set_upgrade') is False: log.err( "FATAL: Something is wrong with the apt system.\n" ) elif Str_SetUpgradeType == "dist-upgrade": log.msg( "\nGenerating database of files that are needed for a dist-upgrade.\n" ) if self.__ExecSystemCmd( '/usr/bin/apt-get -qq --print-uris dist-upgrade >> $__apt_set_upgrade' ) is False: log.err( "FATAL: Something is wrong with the apt system.\n" ) elif Str_SetUpgradeType == "dselect-upgrade": log.msg( "\nGenerating database of files that are needed for a dselect-upgrade.\n" ) if self.__ExecSystemCmd( '/usr/bin/apt-get -qq --print-uris dselect-upgrade >> $__apt_set_upgrade' ) is False: log.err( "FATAL: Something is wrong with the apt system.\n" ) else: log.err( "Invalid upgrade argument type selected\nPlease use one of, upgrade/dist-upgrade/dselect-upgrade\n" ) def __AptInstallPackage(self, PackageList=None, ReleaseType=None): self.package_list = '' self.ReleaseType = ReleaseType for pkg in PackageList: self.package_list += pkg + ', ' log.msg( "\nGenerating database of package %s and its dependencies.\n" % (self.package_list) ) os.environ['__apt_set_install'] = self.WriteTo os.environ['__apt_set_install_packages'] = '' # Build an empty variable #INFO: This is improper way of getting the args, the name of the packages. # But since optparse doesn't have the implementation in place at the moment, we're using it. # Once fixed, this will be changed. # For details look at the parser.add_option line above. for x in PackageList: os.environ['__apt_set_install_packages'] += x + ' ' if self.ReleaseType is not None: os.environ['__apt_set_install_release'] = self.ReleaseType if self.__ExecSystemCmd( '/usr/bin/apt-get -qq --print-uris -t $__apt_set_install_release install $__apt_set_install_packages >> $__apt_set_install' ) is False: log.err( "FATAL: Something is wrong with the apt system.\n" ) else: #FIXME: Find a more Pythonic implementation if self.__ExecSystemCmd( '/usr/bin/apt-get -qq --print-uris install $__apt_set_install_packages >> $__apt_set_install' ) is False: log.err( "FATAL: Something is wrong with the apt system.\n" ) def __AptInstallSrcPackages(self, SrcPackageList=None, ReleaseType=None, BuildDependency=False): self.package_list = '' self.ReleaseType = ReleaseType for pkg in SrcPackageList: self.package_list += pkg + ', ' log.msg( "\nGenerating database of source packages %s.\n" % (self.package_list) ) os.environ['__apt_set_install'] = self.WriteTo os.environ['__apt_set_install_src_packages'] = '' # Build an empty variable for x in SrcPackageList: os.environ['__apt_set_install_src_packages'] += x + ' ' if self.ReleaseType is not None: os.environ['__apt_set_install_release'] = self.ReleaseType if self.__ExecSystemCmd( '/usr/bin/apt-get -qq --print-uris -t $__apt_set_install_release source $__apt_set_install_src_packages >> $__apt_set_install' ) is False: log.err( "FATAL: Something is wrong with the apt system.\n" ) else: #FIXME: Find a more Pythonic implementation if self.__ExecSystemCmd( '/usr/bin/apt-get -qq --print-uris source $__apt_set_install_src_packages >> $__apt_set_install' ) is False: log.err( "FATAL: Something is wrong with the apt system.\n" ) if BuildDependency: log.msg("Generating Build-Dependency for source packages %s.\n" % (self.package_list) ) if self.ReleaseType is not None: os.environ['__apt_set_install_release'] = self.ReleaseType if self.__ExecSystemCmd( '/usr/bin/apt-get -qq --print-uris -t $__apt_set_install_release build-dep $__apt_set_install_src_packages >> $__apt_set_install' ) is False: log.err( "FATAL: Something is wrong with the apt system.\n" ) else: if self.__ExecSystemCmd( '/usr/bin/apt-get -qq --print-uris build-dep $__apt_set_install_src_packages >> $__apt_set_install' ) is False: log.err( "FATAL: Something is wrong with the apt system.\n" ) #FIXME: We'll use python-apt library to make it cleaner. # For now, we need to set markers using shell variables. if os.path.isfile(Str_SetArg): try: os.unlink(Str_SetArg) except OSError: log.err("Cannot remove file %s.\n" % (Str_SetArg) ) #Instantiate Apt based on what we have. For now, fall to apt only AptInst = AptManip(Str_SetArg, Simulate=Bool_TestWindows, AptType="apt") if Bool_SetUpdate: if platform.system() in supported_platforms: if not Bool_TestWindows and os.geteuid() != 0: log.err("This option requires super-user privileges. Execute as root or use sudo/su\n") sys.exit(1) else: AptInst.Update() else: log.err( "This argument is supported only on Unix like systems with apt installed\n" ) sys.exit( 1 ) if Bool_SetUpgrade: if platform.system() in supported_platforms: if not Bool_TestWindows and os.geteuid() != 0: log.err( "This option requires super-user privileges. Execute as root or use sudo/su" ) sys.exit(1) #TODO: Use a more Pythonic way for it if Str_SetUpgradeType == "upgrade": if PythonApt is True: #FIXME: Adapt the new python-apt. Ideas from debdelta log.verbose("Using the python-apt library to generate the database.\n") PythonAptQuery = AptPython() try: install_file = open( Str_SetArg, 'a' ) except IOError: log.err( "Cannot create file %s.\n" % (Str_SetArg) ) sys.exit( 1 ) upgradable = filter( lambda p: p.isUpgradable, PythonAptQuery.cache ) log.msg( "\nGenerating database of files that are needed for an upgrade.\n" ) dup_records = [] for pkg in upgradable: pkg._lookupRecord( True ) dpkg_params = apt_pkg.ParseSection(pkg._records.Record) arch = dpkg_params['Architecture'] path = dpkg_params['Filename'] checksum = dpkg_params['SHA256'] #FIXME: There can be multiple checksum types size = dpkg_params['Size'] cand = pkg._depcache.GetCandidateVer( pkg._pkg ) for ( packagefile, i ) in cand.FileList: indexfile = PythonAptQuery.cache._list.FindIndex( packagefile ) if indexfile: uri = indexfile.ArchiveURI( path ) file = uri.split( '/' )[ - 1] if checksum.__str__() in dup_records: continue install_file.write( uri + ' ' + file + ' ' + size + ' ' + checksum + "\n" ) dup_records.append( checksum.__str__() ) else: AptInst.Upgrade("upgrade", ReleaseType=Str_SetInstallRelease) elif Str_SetUpgradeType == "dist-upgrade": AptInst.Upgrade("dist-upgrade") elif Str_SetUpgradeType == "dselect-upgrade": AptInst.Upgrade("dselect-upgrade") else: log.err( "Invalid upgrade argument type selected\nPlease use one of, upgrade/dist-upgrade/dselect-upgrade\n" ) else: log.err( "This argument is supported only on Unix like systems with apt installed\n" ) sys.exit( 1 ) if List_SetInstallPackages != None and List_SetInstallPackages != []: if platform.system() in supported_platforms: if not Bool_TestWindows and os.geteuid() != 0: log.err( "This option requires super-user privileges. Execute as root or use sudo/su\n" ) sys.exit(1) AptInst.InstallPackages(List_SetInstallPackages, Str_SetInstallRelease) else: log.err( "This argument is supported only on Unix like systems with apt installed\n" ) sys.exit( 1 ) if List_SetInstallSrcPackages != None and List_SetInstallSrcPackages != []: if platform.system() in supported_platforms: AptInst.InstallSrcPackages(List_SetInstallSrcPackages, Str_SetInstallRelease, Bool_SrcBuildDep) else: log.err( "This argument is supported only on Unix like systems with apt installed\n" ) sys.exit( 1 ) class AptPython: def __init__( self ): if PythonApt: self.cache = apt.Cache() def main(): '''Here we basically do the sanity checks, some validations and then accordingly call the corresponding functions. Contains most of the variables that are required by the application to run. Also does command-line option parsing and variable validation.''' # INFO: One way to handle global options in argparse so that they are available to # subparsers also # Global options global_options = argparse.ArgumentParser(add_help=False) global_options.add_argument("--verbose", dest="verbose", help="Enable verbose messages", action="store_true" ) global_options.add_argument("--simulate", dest="simulate", help="Just simulate. Very helpful when debugging", action="store_true" ) if argparse.__version__ >= 1.1: parser = argparse.ArgumentParser( prog=app_name, description="Offline APT Package Manager" + ' - ' + version, epilog=copyright + " - " + terminal_license, parents=[global_options]) parser.add_argument("-v", "--version", action='version', version=version) else: # Remain backward compatible with older argparse versions parser = argparse.ArgumentParser( prog=app_name, version=app_name + " - " + version, description="Offline APT Package Manager", epilog=copyright + " - " + terminal_license, parents=[global_options]) # We need subparsers for set/get/install subparsers = parser.add_subparsers() # SET command options # parser_set = subparsers.add_parser('set', parents=[global_options]) parser_set.set_defaults(func=setter) parser_set.add_argument('set', help="Generate a signature file", action="store", type=str, metavar="apt-offline.sig", default="apt-offline.sig") #TODO: Handle nargs here. parser_set.add_argument("--install-packages", dest="set_install_packages", help="Packages that need to be installed", action="store", type=str, nargs='*', metavar="PKG") parser_set.add_argument("--install-src-packages", dest="set_install_src_packages", help="Source Packages that need to be installed", action="store", type=str, nargs='*', metavar="SOURCE PKG") parser_set.add_argument("--src-build-dep", dest="src_build_dep", help="Install Build Dependency packages for requested source packages", action="store_true") parser_set.add_argument("--release", dest="set_install_release", help="Release target to install packages from", action="store", type=str, metavar="release_name" ) parser_set.add_argument("--update", dest="set_update", help="Generate Signature to update APT Database", action="store_true") parser_set.add_argument("--upgrade", dest="set_upgrade", help="Generate Signature of packages to be upgraded", action="store_true") parser_set.add_argument("--upgrade-type", dest="upgrade_type", help="Type of upgrade to do. Use one of upgrade, dist-upgrade, dselect-ugprade", action="store", type=str, metavar="upgrade", default="upgrade") # GET command options parser_get = subparsers.add_parser('get', parents=[global_options]) #INFO: When get option is called, call the fetcher() function parser_get.set_defaults(func=fetcher) parser_get.add_argument('get', help="Get apt-offline data", action="store", type=str, metavar="apt-offline.sig", default="apt-offline.sig") parser_get.add_argument("--socket-timeout", dest="socket_timeout", help="Set Socket Timeout", action="store", type=int, metavar="30", default=30) parser_get.add_argument("-d", "--download-dir", dest="download_dir", help="Folder path to save files to", action="store", type=str, metavar="apt-downloads") parser_get.add_argument("-s", "--cache-dir", dest="cache_dir", help="Cache folder to search for", action="store", type=str, metavar=".") parser_get.add_argument("--no-checksum", dest="disable_md5check", help="Do not validate checksum of downloaded files", action="store_true") parser_get.add_argument("-t", "--threads", dest="num_of_threads", help="Number of threads to spawn", action="store", type=int, metavar="1", default=1 ) parser_get.add_argument("--bundle", dest="bundle_file", help="Bundle output data to a file", action="store", type=str, metavar="apt-offline-bundle.zip") parser_get.add_argument("--bug-reports", dest="deb_bugs", help="Fetch bug reports from the BTS", action="store_true" ) parser_get.add_argument("--proxy-host", dest="proxy_host", help="Proxy Host to use", type=str, default=None) parser_get.add_argument("--proxy-port", dest="proxy_port", help="Proxy port number to use", type=int, default=None) # INSTALL command options parser_install = subparsers.add_parser('install', parents=[global_options]) parser_install.set_defaults(func=installer) parser_install.add_argument('install', help="Install apt-offline data, a bundle file or a directory", action="store", type=str, metavar="apt-offline-download.zip | apt-offline-download/") parser_install.add_argument("--install-src-path", dest="install_src_path", help="Install src packages to specified path.", default=None) parser_install.add_argument("--skip-bug-reports", dest="skip_bug_reports", help="Skip the bug report check", action="store_true") parser_install.add_argument("--allow-unauthenticated", dest="allow_unauthenticated", help="Ignore apt gpg signatures mismatch", action="store_true") args = parser.parse_args() try: # Sanitize the options/arguments # # Global opts Bool_Verbose = args.verbose Bool_TestWindows = args.simulate global log log = AptOfflineLib.Log( Bool_Verbose, lock=True ) log.verbose(str(args) + "\n") args.func(args) except KeyboardInterrupt: log.err("\nInterrupted by user. Exiting!\n") sys.exit(0) apt-offline-1.3.1/apt_offline_core/AptOfflineFetchBugs.py0000644000000000000000000000421311465207654020312 0ustar #!/usr/bin/env python ############################################################################ # Copyright (C) 2005, 2009 Ritesh Raj Sarraf # # rrs@researchut.com # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 3 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty 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., # # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################ import pypt_core package = raw_input("Please enter the Debian package name: ") package = package.rstrip("\r") file = raw_input("Please enter the filename with full path: ") file = file.rstrip("\r") bugTypes = ["Resolved bugs", "Normal bugs", "Minor bugs", "Wishlist items", "FIXED"] #if pypt_core.FetchBugReportsDebian(package, file) is True: # print "Wrote bug report details for package %s to file %s.\n" % (package, file) BugReportDebian = pypt_core.FetchBugReports(file, bugTypes) if BugReportDebian.FetchBugsDebian(package, file) is True: print "Wrote bug report details for package %s to file %s.\n" % (package, file) else: print "No bugs found for package %s.\n" % (package) apt-offline-1.3.1/apt_offline_core/AptOffline_reportbug_exceptions.py0000644000000000000000000000336411350136350023042 0ustar # Exceptions for reportbug # Written by Chris Lawrence # (C) 2002-04 Chris Lawrence # # This program is freely distributable per the following license: # ## Permission to use, copy, modify, and distribute this software and its ## documentation for any purpose and without fee is hereby granted, ## provided that the above copyright notice appears in all copies and that ## both that copyright notice and this permission notice appear in ## supporting documentation. ## ## I DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL ## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL I ## BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY ## DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, ## WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ## ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS ## SOFTWARE. # # $Id: reportbug_exceptions.py,v 1.3.2.1 2006/08/26 01:57:29 lawrencc Exp $ class reportbug_exception(Exception): pass class reportbug_ui_exception(reportbug_exception): pass # Can't initialize interface class UINotImportable(reportbug_ui_exception): pass # No package found class NoPackage(reportbug_ui_exception): pass # No bugs found class NoBugs(reportbug_ui_exception): pass # Nothing to report class NoReport(reportbug_ui_exception): pass # Code is not implemented class UINotImplemented(reportbug_ui_exception): pass # Other exceptions # No network access class NoNetwork(reportbug_exception): pass # Invalid regular expression class InvalidRegex(reportbug_exception): pass # Lame empty exception used later to save some coding class NoMessage(reportbug_exception): pass apt-offline-1.3.1/apt_offline_core/AptOfflineMagicLib.py0000644000000000000000000016540111350136350020101 0ustar #!/usr/bin/env python ############################################################################ # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 3 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty 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., # # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################ ''' magic.py determines a file type by its magic number (C)opyright 2000 Jason Petrone Command Line Usage: running as `python magic.py file` will print a description of what 'file' is. Module Usage: magic.whatis(data): when passed a string 'data' containing binary or text data, a description of what the data is will be returned. magic.file(filename): returns a description of what the file 'filename' contains. ''' import re, struct, string __version__ = '0.1' magic = [ [0L, 'leshort', '=', 1538L, 'application/x-alan-adventure-game'], [0L, 'string', '=', 'TADS', 'application/x-tads-game'], [0L, 'short', '=', 420L, 'application/x-executable-file'], [0L, 'short', '=', 421L, 'application/x-executable-file'], [0L, 'leshort', '=', 603L, 'application/x-executable-file'], [0L, 'string', '=', 'Core\001', 'application/x-executable-file'], [0L, 'string', '=', 'AMANDA: TAPESTART DATE', 'application/x-amanda-header'], [0L, 'belong', '=', 1011L, 'application/x-executable-file'], [0L, 'belong', '=', 999L, 'application/x-library-file'], [0L, 'belong', '=', 435L, 'video/mpeg'], [0L, 'belong', '=', 442L, 'video/mpeg'], [0L, 'beshort&0xfff0', '=', 65520L, 'audio/mpeg'], [4L, 'leshort', '=', 44817L, 'video/fli'], [4L, 'leshort', '=', 44818L, 'video/flc'], [0L, 'string', '=', 'MOVI', 'video/x-sgi-movie'], [4L, 'string', '=', 'moov', 'video/quicktime'], [4L, 'string', '=', 'mdat', 'video/quicktime'], [0L, 'long', '=', 100554L, 'application/x-apl-workspace'], [0L, 'string', '=', 'FiLeStArTfIlEsTaRt', 'text/x-apple-binscii'], [0L, 'string', '=', '\012GL', 'application/data'], [0L, 'string', '=', 'v\377', 'application/data'], [0L, 'string', '=', 'NuFile', 'application/data'], [0L, 'string', '=', 'N\365F\351l\345', 'application/data'], [0L, 'belong', '=', 333312L, 'application/data'], [0L, 'belong', '=', 333319L, 'application/data'], [257L, 'string', '=', 'ustar\000', 'application/x-tar'], [257L, 'string', '=', 'ustar \000', 'application/x-gtar'], [0L, 'short', '=', 70707L, 'application/x-cpio'], [0L, 'short', '=', 143561L, 'application/x-bcpio'], [0L, 'string', '=', '070707', 'application/x-cpio'], [0L, 'string', '=', '070701', 'application/x-cpio'], [0L, 'string', '=', '070702', 'application/x-cpio'], [0L, 'string', '=', '!\012debian', 'application/x-dpkg'], [0L, 'long', '=', 177555L, 'application/x-ar'], [0L, 'short', '=', 177555L, 'application/data'], [0L, 'long', '=', 177545L, 'application/data'], [0L, 'short', '=', 177545L, 'application/data'], [0L, 'long', '=', 100554L, 'application/x-apl-workspace'], [0L, 'string', '=', '', 'application/x-ar'], [0L, 'string', '=', '!\012__________E', 'application/x-ar'], [0L, 'string', '=', '-h-', 'application/data'], [0L, 'string', '=', '!', 'application/x-ar'], [0L, 'string', '=', '', 'application/x-ar'], [0L, 'string', '=', '', 'application/x-ar'], [0L, 'belong', '=', 1711210496L, 'application/x-ar'], [0L, 'belong', '=', 1013019198L, 'application/x-ar'], [0L, 'long', '=', 557605234L, 'application/x-ar'], [0L, 'lelong', '=', 177555L, 'application/data'], [0L, 'leshort', '=', 177555L, 'application/data'], [0L, 'lelong', '=', 177545L, 'application/data'], [0L, 'leshort', '=', 177545L, 'application/data'], [0L, 'lelong', '=', 236525L, 'application/data'], [0L, 'lelong', '=', 236526L, 'application/data'], [0L, 'lelong&0x8080ffff', '=', 2074L, 'application/x-arc'], [0L, 'lelong&0x8080ffff', '=', 2330L, 'application/x-arc'], [0L, 'lelong&0x8080ffff', '=', 538L, 'application/x-arc'], [0L, 'lelong&0x8080ffff', '=', 794L, 'application/x-arc'], [0L, 'lelong&0x8080ffff', '=', 1050L, 'application/x-arc'], [0L, 'lelong&0x8080ffff', '=', 1562L, 'application/x-arc'], [0L, 'string', '=', '\032archive', 'application/data'], [0L, 'leshort', '=', 60000L, 'application/x-arj'], [0L, 'string', '=', 'HPAK', 'application/data'], [0L, 'string', '=', '\351,\001JAM application/data', ''], [2L, 'string', '=', '-lh0-', 'application/x-lha'], [2L, 'string', '=', '-lh1-', 'application/x-lha'], [2L, 'string', '=', '-lz4-', 'application/x-lha'], [2L, 'string', '=', '-lz5-', 'application/x-lha'], [2L, 'string', '=', '-lzs-', 'application/x-lha'], [2L, 'string', '=', '-lh -', 'application/x-lha'], [2L, 'string', '=', '-lhd-', 'application/x-lha'], [2L, 'string', '=', '-lh2-', 'application/x-lha'], [2L, 'string', '=', '-lh3-', 'application/x-lha'], [2L, 'string', '=', '-lh4-', 'application/x-lha'], [2L, 'string', '=', '-lh5-', 'application/x-lha'], [0L, 'string', '=', 'Rar!', 'application/x-rar'], [0L, 'string', '=', 'SQSH', 'application/data'], [0L, 'string', '=', 'UC2\032', 'application/data'], [0L, 'string', '=', 'PK\003\004', 'application/zip'], [20L, 'lelong', '=', 4257523676L, 'application/x-zoo'], [10L, 'string', '=', '# This is a shell archive', 'application/x-shar'], [0L, 'string', '=', '*STA', 'application/data'], [0L, 'string', '=', '2278', 'application/data'], [0L, 'beshort', '=', 560L, 'application/x-executable-file'], [0L, 'beshort', '=', 561L, 'application/x-executable-file'], [0L, 'string', '=', '\000\004\036\212\200', 'application/core'], [0L, 'string', '=', '.snd', 'audio/basic'], [0L, 'lelong', '=', 6583086L, 'audio/basic'], [0L, 'string', '=', 'MThd', 'audio/midi'], [0L, 'string', '=', 'CTMF', 'audio/x-cmf'], [0L, 'string', '=', 'SBI', 'audio/x-sbi'], [0L, 'string', '=', 'Creative Voice File', 'audio/x-voc'], [0L, 'belong', '=', 1314148939L, 'audio/x-multitrack'], [0L, 'string', '=', 'RIFF', 'audio/x-wav'], [0L, 'string', '=', 'EMOD', 'audio/x-emod'], [0L, 'belong', '=', 779248125L, 'audio/x-pn-realaudio'], [0L, 'string', '=', 'MTM', 'audio/x-multitrack'], [0L, 'string', '=', 'if', 'audio/x-669-mod'], [0L, 'string', '=', 'FAR', 'audio/mod'], [0L, 'string', '=', 'MAS_U', 'audio/x-multimate-mod'], [44L, 'string', '=', 'SCRM', 'audio/x-st3-mod'], [0L, 'string', '=', 'GF1PATCH110\000ID#000002\000', 'audio/x-gus-patch'], [0L, 'string', '=', 'GF1PATCH100\000ID#000002\000', 'audio/x-gus-patch'], [0L, 'string', '=', 'JN', 'audio/x-669-mod'], [0L, 'string', '=', 'UN05', 'audio/x-mikmod-uni'], [0L, 'string', '=', 'Extended Module:', 'audio/x-ft2-mod'], [21L, 'string', '=', '!SCREAM!', 'audio/x-st2-mod'], [1080L, 'string', '=', 'M.K.', 'audio/x-protracker-mod'], [1080L, 'string', '=', 'M!K!', 'audio/x-protracker-mod'], [1080L, 'string', '=', 'FLT4', 'audio/x-startracker-mod'], [1080L, 'string', '=', '4CHN', 'audio/x-fasttracker-mod'], [1080L, 'string', '=', '6CHN', 'audio/x-fasttracker-mod'], [1080L, 'string', '=', '8CHN', 'audio/x-fasttracker-mod'], [1080L, 'string', '=', 'CD81', 'audio/x-oktalyzer-mod'], [1080L, 'string', '=', 'OKTA', 'audio/x-oktalyzer-mod'], [1080L, 'string', '=', '16CN', 'audio/x-taketracker-mod'], [1080L, 'string', '=', '32CN', 'audio/x-taketracker-mod'], [0L, 'string', '=', 'TOC', 'audio/x-toc'], [0L, 'short', '=', 3401L, 'application/x-executable-file'], [0L, 'long', '=', 406L, 'application/x-executable-file'], [0L, 'short', '=', 406L, 'application/x-executable-file'], [0L, 'short', '=', 3001L, 'application/x-executable-file'], [0L, 'lelong', '=', 314L, 'application/x-executable-file'], [0L, 'string', '=', '//', 'text/cpp'], [0L, 'string', '=', '\\\\1cw\\', 'application/data'], [0L, 'string', '=', '\\\\1cw', 'application/data'], [0L, 'belong&0xffffff00', '=', 2231440384L, 'application/data'], [0L, 'belong&0xffffff00', '=', 2231487232L, 'application/data'], [0L, 'short', '=', 575L, 'application/x-executable-file'], [0L, 'short', '=', 577L, 'application/x-executable-file'], [4L, 'string', '=', 'pipe', 'application/data'], [4L, 'string', '=', 'prof', 'application/data'], [0L, 'string', '=', ': shell', 'application/data'], [0L, 'string', '=', '#!/bin/sh', 'application/x-sh'], [0L, 'string', '=', '#! /bin/sh', 'application/x-sh'], [0L, 'string', '=', '#! /bin/sh', 'application/x-sh'], [0L, 'string', '=', '#!/bin/csh', 'application/x-csh'], [0L, 'string', '=', '#! /bin/csh', 'application/x-csh'], [0L, 'string', '=', '#! /bin/csh', 'application/x-csh'], [0L, 'string', '=', '#!/bin/ksh', 'application/x-ksh'], [0L, 'string', '=', '#! /bin/ksh', 'application/x-ksh'], [0L, 'string', '=', '#! /bin/ksh', 'application/x-ksh'], [0L, 'string', '=', '#!/bin/tcsh', 'application/x-csh'], [0L, 'string', '=', '#! /bin/tcsh', 'application/x-csh'], [0L, 'string', '=', '#! /bin/tcsh', 'application/x-csh'], [0L, 'string', '=', '#!/usr/local/tcsh', 'application/x-csh'], [0L, 'string', '=', '#! /usr/local/tcsh', 'application/x-csh'], [0L, 'string', '=', '#!/usr/local/bin/tcsh', 'application/x-csh'], [0L, 'string', '=', '#! /usr/local/bin/tcsh', 'application/x-csh'], [0L, 'string', '=', '#! /usr/local/bin/tcsh', 'application/x-csh'], [0L, 'string', '=', '#!/usr/local/bin/zsh', 'application/x-zsh'], [0L, 'string', '=', '#! /usr/local/bin/zsh', 'application/x-zsh'], [0L, 'string', '=', '#! /usr/local/bin/zsh', 'application/x-zsh'], [0L, 'string', '=', '#!/usr/local/bin/ash', 'application/x-sh'], [0L, 'string', '=', '#! /usr/local/bin/ash', 'application/x-zsh'], [0L, 'string', '=', '#! /usr/local/bin/ash', 'application/x-zsh'], [0L, 'string', '=', '#!/usr/local/bin/ae', 'text/script'], [0L, 'string', '=', '#! /usr/local/bin/ae', 'text/script'], [0L, 'string', '=', '#! /usr/local/bin/ae', 'text/script'], [0L, 'string', '=', '#!/bin/nawk', 'application/x-awk'], [0L, 'string', '=', '#! /bin/nawk', 'application/x-awk'], [0L, 'string', '=', '#! /bin/nawk', 'application/x-awk'], [0L, 'string', '=', '#!/usr/bin/nawk', 'application/x-awk'], [0L, 'string', '=', '#! /usr/bin/nawk', 'application/x-awk'], [0L, 'string', '=', '#! /usr/bin/nawk', 'application/x-awk'], [0L, 'string', '=', '#!/usr/local/bin/nawk', 'application/x-awk'], [0L, 'string', '=', '#! /usr/local/bin/nawk', 'application/x-awk'], [0L, 'string', '=', '#! /usr/local/bin/nawk', 'application/x-awk'], [0L, 'string', '=', '#!/bin/gawk', 'application/x-awk'], [0L, 'string', '=', '#! /bin/gawk', 'application/x-awk'], [0L, 'string', '=', '#! /bin/gawk', 'application/x-awk'], [0L, 'string', '=', '#!/usr/bin/gawk', 'application/x-awk'], [0L, 'string', '=', '#! /usr/bin/gawk', 'application/x-awk'], [0L, 'string', '=', '#! /usr/bin/gawk', 'application/x-awk'], [0L, 'string', '=', '#!/usr/local/bin/gawk', 'application/x-awk'], [0L, 'string', '=', '#! /usr/local/bin/gawk', 'application/x-awk'], [0L, 'string', '=', '#! /usr/local/bin/gawk', 'application/x-awk'], [0L, 'string', '=', '#!/bin/awk', 'application/x-awk'], [0L, 'string', '=', '#! /bin/awk', 'application/x-awk'], [0L, 'string', '=', '#! /bin/awk', 'application/x-awk'], [0L, 'string', '=', '#!/usr/bin/awk', 'application/x-awk'], [0L, 'string', '=', '#! /usr/bin/awk', 'application/x-awk'], [0L, 'string', '=', '#! /usr/bin/awk', 'application/x-awk'], [0L, 'string', '=', 'BEGIN', 'application/x-awk'], [0L, 'string', '=', '#!/bin/perl', 'application/x-perl'], [0L, 'string', '=', '#! /bin/perl', 'application/x-perl'], [0L, 'string', '=', '#! /bin/perl', 'application/x-perl'], [0L, 'string', '=', 'eval "exec /bin/perl', 'application/x-perl'], [0L, 'string', '=', '#!/usr/bin/perl', 'application/x-perl'], [0L, 'string', '=', '#! /usr/bin/perl', 'application/x-perl'], [0L, 'string', '=', '#! /usr/bin/perl', 'application/x-perl'], [0L, 'string', '=', 'eval "exec /usr/bin/perl', 'application/x-perl'], [0L, 'string', '=', '#!/usr/local/bin/perl', 'application/x-perl'], [0L, 'string', '=', '#! /usr/local/bin/perl', 'application/x-perl'], [0L, 'string', '=', '#! /usr/local/bin/perl', 'application/x-perl'], [0L, 'string', '=', 'eval "exec /usr/local/bin/perl', 'application/x-perl'], [0L, 'string', '=', '#!/bin/python', 'application/x-python'], [0L, 'string', '=', '#! /bin/python', 'application/x-python'], [0L, 'string', '=', '#! /bin/python', 'application/x-python'], [0L, 'string', '=', 'eval "exec /bin/python', 'application/x-python'], [0L, 'string', '=', '#!/usr/bin/python', 'application/x-python'], [0L, 'string', '=', '#! /usr/bin/python', 'application/x-python'], [0L, 'string', '=', '#! /usr/bin/python', 'application/x-python'], [0L, 'string', '=', 'eval "exec /usr/bin/python', 'application/x-python'], [0L, 'string', '=', '#!/usr/local/bin/python', 'application/x-python'], [0L, 'string', '=', '#! /usr/local/bin/python', 'application/x-python'], [0L, 'string', '=', '#! /usr/local/bin/python', 'application/x-python'], [0L, 'string', '=', 'eval "exec /usr/local/bin/python', 'application/x-python'], [0L, 'string', '=', '#!/usr/bin/env python', 'application/x-python'], [0L, 'string', '=', '#! /usr/bin/env python', 'application/x-python'], [0L, 'string', '=', '#!/bin/rc', 'text/script'], [0L, 'string', '=', '#! /bin/rc', 'text/script'], [0L, 'string', '=', '#! /bin/rc', 'text/script'], [0L, 'string', '=', '#!/bin/bash', 'application/x-sh'], [0L, 'string', '=', '#! /bin/bash', 'application/x-sh'], [0L, 'string', '=', '#! /bin/bash', 'application/x-sh'], [0L, 'string', '=', '#!/usr/local/bin/bash', 'application/x-sh'], [0L, 'string', '=', '#! /usr/local/bin/bash', 'application/x-sh'], [0L, 'string', '=', '#! /usr/local/bin/bash', 'application/x-sh'], [0L, 'string', '=', '#! /', 'text/script'], [0L, 'string', '=', '#! /', 'text/script'], [0L, 'string', '=', '#!/', 'text/script'], [0L, 'string', '=', '#! text/script', ''], [0L, 'string', '=', '\037\235', 'application/compress'], [0L, 'string', '=', '\037\213', 'application/x-gzip'], [0L, 'string', '=', '\037\036', 'application/data'], [0L, 'short', '=', 17437L, 'application/data'], [0L, 'short', '=', 8191L, 'application/data'], [0L, 'string', '=', '\377\037', 'application/data'], [0L, 'short', '=', 145405L, 'application/data'], [0L, 'string', '=', 'BZh', 'application/x-bzip2'], [0L, 'leshort', '=', 65398L, 'application/data'], [0L, 'leshort', '=', 65142L, 'application/data'], [0L, 'leshort', '=', 64886L, 'application/x-lzh'], [0L, 'string', '=', '\037\237', 'application/data'], [0L, 'string', '=', '\037\236', 'application/data'], [0L, 'string', '=', '\037\240', 'application/data'], [0L, 'string', '=', 'BZ', 'application/x-bzip'], [0L, 'string', '=', '\211LZO\000\015\012\032\012', 'application/data'], [0L, 'belong', '=', 507L, 'application/x-object-file'], [0L, 'belong', '=', 513L, 'application/x-executable-file'], [0L, 'belong', '=', 515L, 'application/x-executable-file'], [0L, 'belong', '=', 517L, 'application/x-executable-file'], [0L, 'belong', '=', 70231L, 'application/core'], [24L, 'belong', '=', 60011L, 'application/data'], [24L, 'belong', '=', 60012L, 'application/data'], [24L, 'belong', '=', 60013L, 'application/data'], [24L, 'belong', '=', 60014L, 'application/data'], [0L, 'belong', '=', 601L, 'application/x-object-file'], [0L, 'belong', '=', 607L, 'application/data'], [0L, 'belong', '=', 324508366L, 'application/x-gdbm'], [0L, 'lelong', '=', 324508366L, 'application/x-gdbm'], [0L, 'string', '=', 'GDBM', 'application/x-gdbm'], [0L, 'belong', '=', 398689L, 'application/x-db'], [0L, 'belong', '=', 340322L, 'application/x-db'], [0L, 'string', '=', '\012\012________64E', 'application/data'], [0L, 'leshort', '=', 387L, 'application/x-executable-file'], [0L, 'leshort', '=', 392L, 'application/x-executable-file'], [0L, 'leshort', '=', 399L, 'application/x-object-file'], [0L, 'string', '=', '\377\377\177', 'application/data'], [0L, 'string', '=', '\377\377|', 'application/data'], [0L, 'string', '=', '\377\377~', 'application/data'], [0L, 'string', '=', '\033c\033', 'application/data'], [0L, 'long', '=', 4553207L, 'image/x11'], [0L, 'string', '=', '!!\012', 'application/x-prof'], [0L, 'short', '=', 1281L, 'application/x-locale'], [24L, 'belong', '=', 60012L, 'application/x-dump'], [24L, 'belong', '=', 60011L, 'application/x-dump'], [24L, 'lelong', '=', 60012L, 'application/x-dump'], [24L, 'lelong', '=', 60011L, 'application/x-dump'], [0L, 'string', '=', '\177ELF', 'application/x-executable-file'], [0L, 'short', '=', 340L, 'application/data'], [0L, 'short', '=', 341L, 'application/x-executable-file'], [1080L, 'leshort', '=', 61267L, 'application/x-linux-ext2fs'], [0L, 'string', '=', '\366\366\366\366', 'application/x-pc-floppy'], [774L, 'beshort', '=', 55998L, 'application/data'], [510L, 'leshort', '=', 43605L, 'application/data'], [1040L, 'leshort', '=', 4991L, 'application/x-filesystem'], [1040L, 'leshort', '=', 5007L, 'application/x-filesystem'], [1040L, 'leshort', '=', 9320L, 'application/x-filesystem'], [1040L, 'leshort', '=', 9336L, 'application/x-filesystem'], [0L, 'string', '=', '-rom1fs-\000', 'application/x-filesystem'], [395L, 'string', '=', 'OS/2', 'application/x-bootable'], [0L, 'string', '=', 'FONT', 'font/x-vfont'], [0L, 'short', '=', 436L, 'font/x-vfont'], [0L, 'short', '=', 17001L, 'font/x-vfont'], [0L, 'string', '=', '%!PS-AdobeFont-1.0', 'font/type1'], [6L, 'string', '=', '%!PS-AdobeFont-1.0', 'font/type1'], [0L, 'belong', '=', 4L, 'font/x-snf'], [0L, 'lelong', '=', 4L, 'font/x-snf'], [0L, 'string', '=', 'STARTFONT font/x-bdf', ''], [0L, 'string', '=', '\001fcp', 'font/x-pcf'], [0L, 'string', '=', 'D1.0\015', 'font/x-speedo'], [0L, 'string', '=', 'flf', 'font/x-figlet'], [0L, 'string', '=', 'flc', 'application/x-font'], [0L, 'belong', '=', 335698201L, 'font/x-libgrx'], [0L, 'belong', '=', 4282797902L, 'font/x-dos'], [7L, 'belong', '=', 4540225L, 'font/x-dos'], [7L, 'belong', '=', 5654852L, 'font/x-dos'], [4098L, 'string', '=', 'DOSFONT', 'font/x-dos'], [0L, 'string', '=', '', 'archive'], [0L, 'string', '=', 'FORM', 'IFF data'], [0L, 'string', '=', 'P1', 'image/x-portable-bitmap'], [0L, 'string', '=', 'P2', 'image/x-portable-graymap'], [0L, 'string', '=', 'P3', 'image/x-portable-pixmap'], [0L, 'string', '=', 'P4', 'image/x-portable-bitmap'], [0L, 'string', '=', 'P5', 'image/x-portable-graymap'], [0L, 'string', '=', 'P6', 'image/x-portable-pixmap'], [0L, 'string', '=', 'IIN1', 'image/tiff'], [0L, 'string', '=', 'MM\000*', 'image/tiff'], [0L, 'string', '=', 'II*\000', 'image/tiff'], [0L, 'string', '=', '\211PNG', 'image/x-png'], [1L, 'string', '=', 'PNG', 'image/x-png'], [0L, 'string', '=', 'GIF8', 'image/gif'], [0L, 'string', '=', '\361\000@\273', 'image/x-cmu-raster'], [0L, 'string', '=', 'id=ImageMagick', 'MIFF image data'], [0L, 'long', '=', 1123028772L, 'Artisan image data'], [0L, 'string', '=', '#FIG', 'FIG image text'], [0L, 'string', '=', 'ARF_BEGARF', 'PHIGS clear text archive'], [0L, 'string', '=', '@(#)SunPHIGS', 'SunPHIGS'], [0L, 'string', '=', 'GKSM', 'GKS Metafile'], [0L, 'string', '=', 'BEGMF', 'clear text Computer Graphics Metafile'], [0L, 'beshort&0xffe0', '=', 32L, 'binary Computer Graphics Metafile'], [0L, 'beshort', '=', 12320L, 'character Computer Graphics Metafile'], [0L, 'string', '=', 'yz', 'MGR bitmap, modern format, 8-bit aligned'], [0L, 'string', '=', 'zz', 'MGR bitmap, old format, 1-bit deep, 16-bit aligned'], [0L, 'string', '=', 'xz', 'MGR bitmap, old format, 1-bit deep, 32-bit aligned'], [0L, 'string', '=', 'yx', 'MGR bitmap, modern format, squeezed'], [0L, 'string', '=', '%bitmap\000', 'FBM image data'], [1L, 'string', '=', 'PC Research, Inc', 'group 3 fax data'], [0L, 'beshort', '=', 65496L, 'image/jpeg'], [0L, 'string', '=', 'hsi1', 'image/x-jpeg-proprietary'], [0L, 'string', '=', 'BM', 'image/x-bmp'], [0L, 'string', '=', 'IC', 'image/x-ico'], [0L, 'string', '=', 'PI', 'PC pointer image data'], [0L, 'string', '=', 'CI', 'PC color icon data'], [0L, 'string', '=', 'CP', 'PC color pointer image data'], [0L, 'string', '=', '/* XPM */', 'X pixmap image text'], [0L, 'leshort', '=', 52306L, 'RLE image data,'], [0L, 'string', '=', 'Imagefile version-', 'iff image data'], [0L, 'belong', '=', 1504078485L, 'x/x-image-sun-raster'], [0L, 'beshort', '=', 474L, 'x/x-image-sgi'], [0L, 'string', '=', 'IT01', 'FIT image data'], [0L, 'string', '=', 'IT02', 'FIT image data'], [2048L, 'string', '=', 'PCD_IPI', 'x/x-photo-cd-pack-file'], [0L, 'string', '=', 'PCD_OPA', 'x/x-photo-cd-overfiew-file'], [0L, 'string', '=', 'SIMPLE =', 'FITS image data'], [0L, 'string', '=', 'This is a BitMap file', 'Lisp Machine bit-array-file'], [0L, 'string', '=', '!!', 'Bennet Yee\'s "face" format'], [0L, 'beshort', '=', 4112L, 'PEX Binary Archive'], [3000L, 'string', '=', 'Visio (TM) Drawing', '%s'], [0L, 'leshort', '=', 502L, 'basic-16 executable'], [0L, 'leshort', '=', 503L, 'basic-16 executable (TV)'], [0L, 'leshort', '=', 510L, 'application/x-executable-file'], [0L, 'leshort', '=', 511L, 'application/x-executable-file'], [0L, 'leshort', '=', 512L, 'application/x-executable-file'], [0L, 'leshort', '=', 522L, 'application/x-executable-file'], [0L, 'leshort', '=', 514L, 'application/x-executable-file'], [0L, 'string', '=', '\210OPS', 'Interleaf saved data'], [0L, 'string', '=', '', 'Compiled SGML rules file'], [0L, 'string', '=', '', 'A/E SGML Document binary'], [0L, 'string', '=', '', 'A/E SGML binary styles file'], [0L, 'short', '=', 49374L, 'Compiled PSI (v1) data'], [0L, 'short', '=', 49370L, 'Compiled PSI (v2) data'], [0L, 'short', '=', 125252L, 'SoftQuad DESC or font file binary'], [0L, 'string', '=', 'SQ BITMAP1', 'SoftQuad Raster Format text'], [0L, 'string', '=', 'X SoftQuad', 'troff Context intermediate'], [0L, 'belong&077777777', '=', 600413L, 'sparc demand paged'], [0L, 'belong&077777777', '=', 600410L, 'sparc pure'], [0L, 'belong&077777777', '=', 600407L, 'sparc'], [0L, 'belong&077777777', '=', 400413L, 'mc68020 demand paged'], [0L, 'belong&077777777', '=', 400410L, 'mc68020 pure'], [0L, 'belong&077777777', '=', 400407L, 'mc68020'], [0L, 'belong&077777777', '=', 200413L, 'mc68010 demand paged'], [0L, 'belong&077777777', '=', 200410L, 'mc68010 pure'], [0L, 'belong&077777777', '=', 200407L, 'mc68010'], [0L, 'belong', '=', 407L, 'old sun-2 executable'], [0L, 'belong', '=', 410L, 'old sun-2 pure executable'], [0L, 'belong', '=', 413L, 'old sun-2 demand paged executable'], [0L, 'belong', '=', 525398L, 'SunOS core file'], [0L, 'long', '=', 4197695630L, 'SunPC 4.0 Hard Disk'], [0L, 'string', '=', '#SUNPC_CONFIG', 'SunPC 4.0 Properties Values'], [0L, 'string', '=', 'snoop', 'Snoop capture file'], [36L, 'string', '=', 'acsp', 'Kodak Color Management System, ICC Profile'], [0L, 'string', '=', '#!teapot\012xdr', 'teapot work sheet (XDR format)'], [0L, 'string', '=', '\032\001', 'Compiled terminfo entry'], [0L, 'short', '=', 433L, 'Curses screen image'], [0L, 'short', '=', 434L, 'Curses screen image'], [0L, 'string', '=', '\367\002', 'TeX DVI file'], [0L, 'string', '=', '\367\203', 'font/x-tex'], [0L, 'string', '=', '\367Y', 'font/x-tex'], [0L, 'string', '=', '\367\312', 'font/x-tex'], [0L, 'string', '=', 'This is TeX,', 'TeX transcript text'], [0L, 'string', '=', 'This is METAFONT,', 'METAFONT transcript text'], [2L, 'string', '=', '\000\021', 'font/x-tex-tfm'], [2L, 'string', '=', '\000\022', 'font/x-tex-tfm'], [0L, 'string', '=', '\\\\input\\', 'texinfo Texinfo source text'], [0L, 'string', '=', 'This is Info file', 'GNU Info text'], [0L, 'string', '=', '\\\\input', 'TeX document text'], [0L, 'string', '=', '\\\\section', 'LaTeX document text'], [0L, 'string', '=', '\\\\setlength', 'LaTeX document text'], [0L, 'string', '=', '\\\\documentstyle', 'LaTeX document text'], [0L, 'string', '=', '\\\\chapter', 'LaTeX document text'], [0L, 'string', '=', '\\\\documentclass', 'LaTeX 2e document text'], [0L, 'string', '=', '\\\\relax', 'LaTeX auxiliary file'], [0L, 'string', '=', '\\\\contentsline', 'LaTeX table of contents'], [0L, 'string', '=', '\\\\indexentry', 'LaTeX raw index file'], [0L, 'string', '=', '\\\\begin{theindex}', 'LaTeX sorted index'], [0L, 'string', '=', '\\\\glossaryentry', 'LaTeX raw glossary'], [0L, 'string', '=', '\\\\begin{theglossary}', 'LaTeX sorted glossary'], [0L, 'string', '=', 'This is makeindex', 'Makeindex log file'], [0L, 'string', '=', '**TI82**', 'TI-82 Graphing Calculator'], [0L, 'string', '=', '**TI83**', 'TI-83 Graphing Calculator'], [0L, 'string', '=', '**TI85**', 'TI-85 Graphing Calculator'], [0L, 'string', '=', '**TI92**', 'TI-92 Graphing Calculator'], [0L, 'string', '=', '**TI80**', 'TI-80 Graphing Calculator File.'], [0L, 'string', '=', '**TI81**', 'TI-81 Graphing Calculator File.'], [0L, 'string', '=', 'TZif', 'timezone data'], [0L, 'string', '=', '\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001\000', 'old timezone data'], [0L, 'string', '=', '\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\002\000', 'old timezone data'], [0L, 'string', '=', '\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\003\000', 'old timezone data'], [0L, 'string', '=', '\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\004\000', 'old timezone data'], [0L, 'string', '=', '\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\005\000', 'old timezone data'], [0L, 'string', '=', '\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\006\000', 'old timezone data'], [0L, 'string', '=', '.\\\\"', 'troff or preprocessor input text'], [0L, 'string', '=', '\'\\\\"', 'troff or preprocessor input text'], [0L, 'string', '=', '\'.\\\\"', 'troff or preprocessor input text'], [0L, 'string', '=', '\\\\"', 'troff or preprocessor input text'], [0L, 'string', '=', 'x T', 'ditroff text'], [0L, 'string', '=', '@\357', 'very old (C/A/T) troff output data'], [0L, 'string', '=', 'Interpress/Xerox', 'Xerox InterPress data'], [0L, 'short', '=', 263L, 'unknown machine executable'], [0L, 'short', '=', 264L, 'unknown pure executable'], [0L, 'short', '=', 265L, 'PDP-11 separate I&D'], [0L, 'short', '=', 267L, 'unknown pure executable'], [0L, 'long', '=', 268L, 'unknown demand paged pure executable'], [0L, 'long', '=', 269L, 'unknown demand paged pure executable'], [0L, 'long', '=', 270L, 'unknown readable demand paged pure executable'], [0L, 'string', '=', 'begin uuencoded', 'or xxencoded text'], [0L, 'string', '=', 'xbtoa Begin', "btoa'd text"], [0L, 'string', '=', '$\012ship', "ship'd binary text"], [0L, 'string', '=', 'Decode the following with bdeco', 'bencoded News text'], [11L, 'string', '=', 'must be converted with BinHex', 'BinHex binary text'], [0L, 'short', '=', 610L, 'Perkin-Elmer executable'], [0L, 'beshort', '=', 572L, 'amd 29k coff noprebar executable'], [0L, 'beshort', '=', 1572L, 'amd 29k coff prebar executable'], [0L, 'beshort', '=', 160007L, 'amd 29k coff archive'], [6L, 'beshort', '=', 407L, 'unicos (cray) executable'], [596L, 'string', '=', 'X\337\377\377', 'Ultrix core file'], [0L, 'string', '=', 'Joy!peffpwpc', 'header for PowerPC PEF executable'], [0L, 'lelong', '=', 101557L, 'VAX single precision APL workspace'], [0L, 'lelong', '=', 101556L, 'VAX double precision APL workspace'], [0L, 'lelong', '=', 407L, 'VAX executable'], [0L, 'lelong', '=', 410L, 'VAX pure executable'], [0L, 'lelong', '=', 413L, 'VAX demand paged pure executable'], [0L, 'leshort', '=', 570L, 'VAX COFF executable'], [0L, 'leshort', '=', 575L, 'VAX COFF pure executable'], [0L, 'string', '=', 'LBLSIZE=', 'VICAR image data'], [43L, 'string', '=', 'SFDU_LABEL', 'VICAR label file'], [0L, 'short', '=', 21845L, 'VISX image file'], [0L, 'string', '=', '\260\0000\000', 'VMS VAX executable'], [0L, 'belong', '=', 50331648L, 'VMS Alpha executable'], [1L, 'string', '=', 'WPC', '(Corel/WP)'], [0L, 'string', '=', 'core', 'core file (Xenix)'], [0L, 'byte', '=', 128L, '8086 relocatable (Microsoft)'], [0L, 'leshort', '=', 65381L, 'x.out'], [0L, 'leshort', '=', 518L, 'Microsoft a.out'], [0L, 'leshort', '=', 320L, 'old Microsoft 8086 x.out'], [0L, 'lelong', '=', 518L, 'b.out'], [0L, 'leshort', '=', 1408L, 'XENIX 8086 relocatable or 80286 small model'], [0L, 'long', '=', 59399L, 'object file (z8000 a.out)'], [0L, 'long', '=', 59400L, 'pure object file (z8000 a.out)'], [0L, 'long', '=', 59401L, 'separate object file (z8000 a.out)'], [0L, 'long', '=', 59397L, 'overlay object file (z8000 a.out)'], [0L, 'string', '=', 'ZyXEL\002', 'ZyXEL voice data'], ] magicNumbers = [] def strToNum(n): val = 0 col = long(1) if n[:1] == 'x': n = '0' + n if n[:2] == '0x': # hex n = string.lower(n[2:]) while len(n) > 0: l = n[len(n) - 1] val = val + string.hexdigits.index(l) * col col = col * 16 n = n[:len(n)-1] elif n[0] == '\\': # octal n = n[1:] while len(n) > 0: l = n[len(n) - 1] if ord(l) < 48 or ord(l) > 57: break val = val + int(l) * col col = col * 8 n = n[:len(n)-1] else: val = string.atol(n) return val def unescape(s): # replace string escape sequences while 1: m = re.search(r'\\', s) if not m: break x = m.start()+1 if m.end() == len(s): # escaped space at end s = s[:len(s)-1] + ' ' elif s[x:x+2] == '0x': # hex ascii value c = chr(strToNum(s[x:x+4])) s = s[:x-1] + c + s[x+4:] elif s[m.start()+1] == 'x': # hex ascii value c = chr(strToNum(s[x:x+3])) s = s[:x-1] + c + s[x+3:] elif ord(s[x]) > 47 and ord(s[x]) < 58: # octal ascii value end = x while (ord(s[end]) > 47 and ord(s[end]) < 58): end = end + 1 if end > len(s) - 1: break c = chr(strToNum(s[x-1:end])) s = s[:x-1] + c + s[end:] elif s[x] == 'n': # newline s = s[:x-1] + '\n' + s[x+1:] else: break return s class magicTest: def __init__(self, offset, t, op, value, msg, mask = None): if t.count('&') > 0: mask = strToNum(t[t.index('&')+1:]) t = t[:t.index('&')] if type(offset) == type('a'): self.offset = strToNum(offset) else: self.offset = offset self.type = t self.msg = msg self.subTests = [] self.op = op self.mask = mask self.value = value def test(self, data): if self.mask: data = data & self.mask if self.op == '=': if self.value == data: return self.msg elif self.op == '<': pass elif self.op == '>': pass elif self.op == '&': pass elif self.op == '^': pass return None def compare(self, data): #print str([self.type, self.value, self.msg]) try: if self.type == 'string': c = ''; s = '' for i in range(0, len(self.value)+1): if i + self.offset > len(data) - 1: break s = s + c [c] = struct.unpack('c', data[self.offset + i]) data = s elif self.type == 'short': [data] = struct.unpack('h', data[self.offset : self.offset + 2]) elif self.type == 'leshort': [data] = struct.unpack('H', data[self.offset : self.offset + 2]) elif self.type == 'long': [data] = struct.unpack('l', data[self.offset : self.offset + 4]) elif self.type == 'lelong': [data] = struct.unpack('l', data[self.offset : self.offset + 4]) else: #print 'UNKNOWN TYPE: ' + self.type pass except: return None # print str([self.msg, self.value, data]) return self.test(data) def load(file): global magicNumbers lines = open(file).readlines() last = { 0: None } for line in lines: if re.match(r'\s*#', line): # comment continue else: # split up by space delimiters, and remove trailing space line = string.rstrip(line) line = re.split(r'\s*', line) if len(line) < 3: # bad line continue offset = line[0] type = line[1] value = line[2] level = 0 while offset[0] == '>': # count the level of the type level = level + 1 offset = offset[1:] l = magicNumbers if level > 0: l = last[level - 1].subTests if offset[0] == '(': # don't handle indirect offsets just yet print 'SKIPPING ' + string.join(list(line[3:])) pass elif offset[0] == '&': # don't handle relative offsets just yet print 'SKIPPING ' + string.join(list(line[3:])) pass else: operands = ['=', '<', '>', '&'] if operands.count(value[0]) > 0: # a comparison operator is specified op = value[0] value = value[1:] else: print str([value, operands]) if len(value) >1 and value[0] == '\\' and operands.count(value[1]) >0: # literal value that collides with operands is escaped value = value[1:] op = '=' mask = None if type == 'string': while 1: value = unescape(value) if value[len(value)-1] == ' ' and len(line) > 3: # last value was an escaped space, join value = value + line[3] del line[3] else: break else: if value.count('&') != 0: mask = value[(value.index('&') + 1):] print 'MASK: ' + mask value = value[:(value.index('&')+1)] try: value = strToNum(value) except: continue msg = string.join(list(line[3:])) new = magicTest(offset, type, op, value, msg, mask) last[level] = new l.append(new) def whatis(data): for test in magicNumbers: m = test.compare(data) if m: return m # no matching, magic number. is it binary or text? for c in data: if ord(c) > 128: return 'data' # its ASCII, now do text tests if string.find('The', data, 0, 8192) > -1: return 'English text' if string.find('def', data, 0, 8192) > -1: return 'Python Source' return 'ASCII text' def file(file): try: return whatis(open(file, 'r').read(8192)) except Exception, e: if str(e) == '[Errno 21] Is a directory': return 'directory' else: raise e #### BUILD DATA #### #load('mime-magic') #f = open('out', 'w') #for m in magicNumbers: # f.write(str([m.offset, m.type, m.op, m.value, m.msg]) + ',\n') #f.close import sys for m in magic: magicNumbers.append(magicTest(m[0], m[1], m[2], m[3], m[4])) if __name__ == '__main__': import sys for arg in sys.argv[1:]: msg = file(arg) if msg: print arg + ': ' + msg else: print arg + ': unknown'apt-offline-1.3.1/apt_offline_core/AptOfflineDebianBtsLib.py0000644000000000000000000007623611547566433020744 0ustar # # debianbts.py - Routines to deal with the debbugs web pages # # Written by Chris Lawrence # (C) 1999-2006 Chris Lawrence # # This program is freely distributable per the following license: # ## Permission to use, copy, modify, and distribute this software and its ## documentation for any purpose and without fee is hereby granted, ## provided that the above copyright notice appears in all copies and that ## both that copyright notice and this permission notice appear in ## supporting documentation. ## ## I DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL ## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL I ## BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY ## DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, ## WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ## ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS ## SOFTWARE. # # Version 3.35; see changelog for revision history # # $Id: debianbts.py,v 1.24.2.7 2006/10/16 17:14:03 lawrencc Exp $ import sgmllib, glob, os, re, rfc822, time, urllib from AptOffline_urlutils import open_url from AptOffline_reportbug_exceptions import NoNetwork import sys import mailbox import email import email.Errors import cStringIO import cgi def msgfactory(fp): try: return email.message_from_file(fp) except email.Errors.MessageParseError: # Don't return None since that will # stop the mailbox iterator return '' class Error(Exception): pass # Severity levels SEVERITIES = { 'critical' : """makes unrelated software on the system (or the whole system) break, or causes serious data loss, or introduces a security hole on systems where you install the package.""", 'grave' : """makes the package in question unusable by most or all users, or causes data loss, or introduces a security hole allowing access to the accounts of users who use the package.""", 'serious' : """is a severe violation of Debian policy (that is, the problem is a violation of a 'must' or 'required' directive); may or may not affect the usability of the package. Note that non-severe policy violations may be 'normal,' 'minor,' or 'wishlist' bugs. (Package maintainers may also designate other bugs as 'serious' and thus release-critical; however, end users should not do so.)""", 'important' : """a bug which has a major effect on the usability of a package, without rendering it completely unusable to everyone.""", 'does-not-build' : """a bug that stops the package from being built from source. (This is a 'virtual severity'.)""", 'normal' : """a bug that does not undermine the usability of the whole package; for example, a problem with a particular option or menu item.""", 'minor' : """things like spelling mistakes and other minor cosmetic errors that do not affect the core functionality of the package.""", 'wishlist' : "suggestions and requests for new features.", } # justifications for critical bugs JUSTIFICATIONS = { 'critical' : ( ('breaks unrelated software', """breaks unrelated software on the system (packages that have a dependency relationship are not unrelated)"""), ('breaks the whole system', """renders the entire system unusable (e.g., unbootable, unable to reach a multiuser runlevel, etc.)"""), ('causes serious data loss', """causes loss of important, irreplaceable data"""), ('root security hole', """introduces a security hole allowing access to root (or another privileged system account), or data normally accessible only by such accounts"""), ('unknown', """not sure, or none of the above"""), ), 'grave' : ( ('renders package unusable', """renders the package unusable, or mostly so, on all or nearly all possible systems on which it could be installed (i.e., not a hardware-specific bug); or renders package uninstallable or unremovable without special effort"""), ('causes non-serious data loss', """causes the loss of data on the system that is unimportant, or restorable without resorting to backup media"""), ('user security hole', """introduces a security hole allowing access to user accounts or data not normally accessible"""), ('unknown', """not sure, or none of the above"""), ) } # Ordering for justifications JUSTORDER = { 'critical' : ['breaks unrelated software', 'breaks the whole system', 'causes serious data loss', 'root security hole', 'unknown'], 'grave' : ['renders package unusable', 'causes non-serious data loss', 'user security hole', 'unknown'] } SEVERITIES_gnats = { 'critical' : 'The product, component or concept is completely' 'non-operational or some essential functionality is missing. No' 'workaround is known.', 'serious' : 'The product, component or concept is not working' 'properly or significant functionality is missing. Problems that' 'would otherwise be considered ''critical'' are rated ''serious'' when' 'a workaround is known.', 'non-critical' : 'The product, component or concept is working' 'in general, but lacks features, has irritating behavior, does' 'something wrong, or doesn''t match its documentation.', } # Rank order of severities, for sorting SEVLIST = ['critical', 'grave', 'serious', 'important', 'does-not-build', 'normal', 'non-critical', 'minor', 'wishlist', 'fixed'] def convert_severity(severity, type='debbugs'): "Convert severity names if needed." if type == 'debbugs': return {'non-critical' : 'normal'}.get(severity, severity) elif type == 'gnats': return {'grave' : 'critical', 'important' : 'serious', 'normal' : 'non-critical', 'minor' : 'non-critical', 'wishlist' : 'non-critical'}.get(severity, severity) else: return severity # These packages are virtual in Debian; we don't look them up... debother = { 'base' : 'General bugs in the base system', # Actually a real package, but most people don't have boot-floppies installed for good reason # 'boot-floppy' : '(Obsolete, please use boot-floppies instead.)', 'boot-floppies' : 'Bugs in the woody installation subsystem', 'bugs.debian.org' : 'The bug tracking system, @bugs.debian.org', 'cdimage.debian.org' : 'CD Image issues', 'cdrom' : 'Problems with installation from CD-ROMs', # dpkg-iwj -- The dpkg branch maintained by Ian Jackson 'debian-policy' : 'Proposed changes in the Debian policy documentation', 'ftp.debian.org' : 'Problems with the FTP site', 'general' : 'General problems (e.g., that many manpages are mode 755)', 'install' : 'Problems with the sarge installer.', 'installation' : 'General installation problems not covered otherwise.', # 'kernel' : '(Obsolete, please use "linux-image" instead.)', 'linux-image' : 'Problems with the Linux kernel, or the kernel shipped with Debian', 'listarchives' : 'Problems with the WWW mailing list archives', 'lists.debian.org' : 'The mailing lists, debian-*@lists.debian.org.', 'mirrors' : 'Problems with Debian archive mirrors.', 'nonus.debian.org' : 'Problems with the non-US FTP site', 'press' : 'Press release issues', 'project' : 'Problems related to Project administration', 'qa.debian.org' : 'Problems related to the quality assurance group', #slink-cd -- Slink CD #spam -- Spam (reassign spam to here so we can complain about it) 'security.debian.org' : 'Problems with the security updates server', 'upgrade-reports' : 'Reports of successful and unsucessful upgrades', 'wnpp' : 'Work-Needing and Prospective Packages list', 'www.debian.org' : 'Problems with the WWW site (including other *.debian.org sites)' } progenyother = { 'debian-general' : 'Any non-package-specific bug', } def handle_wnpp(package, bts, ui, fromaddr, online=True, http_proxy=None): desc = body = '' headers = [] pseudos = [] query = True tag = ui.menu('What sort of request is this? (If none of these ' 'things mean anything to you, or you are trying to report ' 'a bug in an existing package, please press Enter to ' 'exit reportbug.)', { 'O' : "The package has been `Orphaned'. It needs a new maintainer as soon as possible.", 'RFA' : "This is a `Request for Adoption'. Due to lack of time, resources, interest or something similar, the current maintainer is asking for someone else to maintain this package. He/she will maintain it in the meantime, but perhaps not in the best possible way. In short: the package needs a new maintainer.", 'RFH' : "This is a `Request For Help'. The current maintainer wants to continue to maintain this package, but he/she needs some help to do this, because his/her time is limited or the package is quite big and needs several maintainers.", 'ITP' : "This is an `Intent To Package'. Please submit a package description along with copyright and URL in such a report.", 'RFP' : "This is a `Request For Package'. You have found an interesting piece of software and would like someone else to maintain it for Debian. Please submit a package description along with copyright and URL in such a report.", }, 'Choose the request type: ', empty_ok=True) if not tag: ui.long_message('To report a bug in a package, use the name of the package, not wnpp.\n') raise SystemExit if tag in ('RFP', 'ITP'): prompt = 'Please enter the proposed package name: ' else: prompt = 'Please enter the name of the package: ' package = ui.get_string(prompt) if not package: return ui.ewrite('Checking status database...\n') info = reportbug.get_package_status(package) available = info[1] severity = 'normal' if tag in ('ITP', 'RFP'): if available and (not online or checkversions.check_available( package, '0', http_proxy=http_proxy)): if not ui.yes_no( ('A package called %s already appears to exist (at least on ' 'your system); continue?' % package), 'Ignore this problem and continue. If you have ' 'already locally created a package with this name, this ' 'warning message may have been produced in error.', 'Exit without filing a report.', default=0): sys.exit(1) severity = 'wishlist' desc = ui.get_string( 'Please briefly describe this package; this should be an ' 'appropriate short description for the eventual package: ') if not desc: return if tag == 'ITP': headers.append('X-Debbugs-CC: debian-devel@lists.debian.org') pseudos.append('Owner: %s' % fromaddr) ui.ewrite('Your report will be carbon-copied to debian-devel, ' 'per Debian policy.\n') body = """* Package name : %s Version : x.y.z Upstream Author : Name * URL : http://www.example.org/ * License : (GPL, LGPL, BSD, MIT/X, etc.) Programming Lang: (C, C++, C#, Perl, Python, etc.) Description : %s (Include the long description here.) """ % (package, desc) elif tag in ('O', 'RFA', 'RFH'): severity = 'normal' query = False if not available: info = reportbug.get_source_package(package) if info: info = reportbug.get_package_status(info[0][0]) if not info: cont = ui.select_options( "This package doesn't appear to exist; continue?", 'yN', {'y': 'Ignore this problem and continue.', 'n': 'Exit without filing a report.' }) if cont == 'n': sys.exit(1) desc = fulldesc = '' else: desc = info[11] or '' package = info[12] or package fulldesc = info[13] if tag == 'O' and info and info[9] in \ ('required', 'important', 'standard'): severity = 'important' if tag == 'RFH': headers.append('X-Debbugs-CC: debian-devel@lists.debian.org') ui.ewrite('Your request will be carbon-copied to debian-devel, ' 'per Debian policy.\n') if fulldesc: orphstr = 'intend to orphan' if tag == 'RFA': orphstr = 'request an adopter for' elif tag == 'RFH': orphstr = 'request assistance with maintaining' body = ('I %s the %s package.\n\n' 'The package description is:\n') % (orphstr, package) body = body + fulldesc + '\n' if desc: subject = '%s: %s -- %s' % (tag, package, desc) else: subject = '%s: %s' % (tag, package) return (subject, severity, headers, pseudos, body, query) # Supported servers # Theoretically support for GNATS and Jitterbug could be added here. SYSTEMS = { 'debian' : { 'name' : 'Debian', 'email': '%s@bugs.debian.org', 'btsroot' : 'http://www.debian.org/Bugs/', 'otherpkgs' : debother, 'nonvirtual' : ['linux-image', 'kernel-image'], 'specials' : { 'wnpp': handle_wnpp }, # Dependency packages 'deppkgs' : ('gcc', 'g++', 'cpp', 'gcj', 'gpc', 'gobjc', 'chill', 'gij', 'g77', 'python', 'python-base', 'x-window-system-core', 'x-window-system'), 'cgiroot' : 'http://bugs.debian.org/cgi-bin/' }, 'kde' : { 'name' : 'KDE Project', 'email': '%s@bugs.kde.org', 'btsroot': 'http://bugs.kde.org/' }, 'mandrake' : { 'name' : 'Linux-Mandrake', 'email': '%s@bugs.linux-mandrake.com', 'type' : 'mailto', 'query-dpkg' : False }, 'gnome' : { 'name' : 'GNOME Project', 'email': '%s@bugs.gnome.org', 'type' : 'mailto', 'query-dpkg' : False }, 'ximian' : { 'name' : 'Ximian', 'email': '%s@bugs.ximian.com', 'type' : 'mailto' }, 'progeny' : { 'name' : 'Progeny', 'email' : 'bugs@progeny.com', 'type' : 'gnats', 'otherpkgs' : progenyother }, 'ubuntu' : { 'name' : 'Ubuntu', 'email' : 'ubuntu-users@lists.ubuntu.com', 'type' : 'mailto' }, 'guug' : { 'name' : 'GUUG (German Unix User Group)', 'email' : '%s@bugs.guug.de', 'query-dpkg' : False }, 'grml' : { 'name' : 'grml', 'email': '%s@bugs.grml.org', 'btsroot' : 'http://bugs.grml.org/', 'cgiroot' : 'http://bugs.grml.org/cgi-bin/' }, } SYSTEMS['helixcode'] = SYSTEMS['ximian'] CLASSES = { 'sw-bug' : 'The problem is a bug in the software or code. For' 'example, a crash would be a sw-bug.', 'doc-bug' : 'The problem is in the documentation. For example,' 'an error in a man page would be a doc-bug.', 'change-request' : 'You are requesting a new feature or a change' 'in the behavior of software, or are making a suggestion. For' 'example, if you wanted reportbug to be able to get your local' 'weather forecast, as well as report bugs, that would be a' 'change-request.', } CLASSLIST = ['sw-bug', 'doc-bug', 'change-request'] CRITICAL_TAGS = { 'security' : 'This problem is a security vulnerability in Debian.', } TAGS = { 'patch' : 'You are including a patch to fix this problem.', ## 'upstream' : 'You believe this problem is not specific to Debian.', ## 'potato' : 'This bug only applies to the potato release (Debian 2.2).', ## 'woody' : 'This bug only applies to the woody release (Debian 3.0).', ## 'sarge' : 'This bug only applies to the sarge release (Debian 3.1).', ## 'sid' : 'This bug only applies to the unstable branch of Debian.', "l10n" : "This bug reports a localization/internationalization issue.", ## 'done' : 'No more tags.', } EXTRA_TAGS = ['potato', 'woody', 'sarge', 'security', 'sid', 'upstream'] TAGLIST = ['l10n', 'patch'] CRITICAL_TAGLIST = ['security'] def yn_bool(setting): if setting: if str(setting) == 'no': return 'no' return 'yes' else: return 'no' def cgi_report_url(system, number, archived=False, mbox=False): root = SYSTEMS[system].get('cgiroot') if root: return '%sbugreport.cgi?bug=%d&archived=%s&mbox=%s' % ( root, number, archived, yn_bool(mbox)) return None def cgi_package_url(system, package, archived=False, source=False, repeatmerged=True, version=None): root = SYSTEMS[system].get('cgiroot') if not root: return None #package = urllib.quote_plus(package.lower()) if source: query = {'src' : package.lower()} else: query = {'pkg' : package.lower()} query['repeatmerged'] = yn_bool(repeatmerged) query['archived'] = yn_bool(archived) if version: query['version'] = str(version) qstr = urllib.urlencode(query) #print qstr return '%spkgreport.cgi?%s' % (root, qstr) def package_url(system, package, mirrors=None, source=False, repeatmerged=True): btsroot=get_btsroot(system, mirrors) package = urllib.quote_plus(package.lower()) return btsroot+('db/pa/l%s.html' % package) def report_url(system, number, mirrors=None): number = str(number) if len(number) < 2: return None btsroot=get_btsroot(system, mirrors) return btsroot+('db/%s/%s.html' % (number[:2], number)) def get_package_url(system, package, mirrors=None, source=False, archived=False, repeatmerged=True): return (cgi_package_url(system, package, archived, source, repeatmerged) or package_url(system, package, mirrors, source, repeatmerged)) def get_report_url(system, number, mirrors=None, archived=False, mbox=False): return (cgi_report_url(system, number, archived, mbox) or report_url(system, number, mirrors)) def parse_bts_url(url): bits = url.split(':', 1) if len(bits) != 2: return None type, loc = bits if loc.startswith('//'): loc = loc[2:] while loc.endswith('/'): loc = loc[:-1] return type, loc # Dynamically add any additional systems found for origin in glob.glob('/etc/dpkg/origins/*'): try: fp = file(origin) system = os.path.basename(origin) SYSTEMS[system] = SYSTEMS.get(system, { 'otherpkgs' : {}, 'query-dpkg' : True, 'mirrors' : {}, 'cgiroot' : None } ) for line in fp: try: (header, content) = line.split(': ', 1) header = header.lower() content = content.strip() if header == 'vendor': SYSTEMS[system]['name'] = content elif header == 'bugs': (type, root) = parse_bts_url(content) SYSTEMS[system]['type'] = type if type == 'debbugs': SYSTEMS[system]['btsroot'] = 'http://'+root+'/' SYSTEMS[system]['email'] = '%s@'+root elif type == 'mailto': SYSTEMS[system]['btsroot'] = None SYSTEMS[system]['email'] = root else: # We don't know what to do... pass except ValueError: pass fp.close() except IOError: pass # For summary pages, we want to keep: # # - Contents of ... # - Contents of

...

# - Contents of each
  • # # For individual bugs, we want to keep: # - Contents of ... # - Contents of every
    ...
    after a

    ....

    tag. class BTSParser(sgmllib.SGMLParser): def __init__(self, mode='summary', cgi=False, followups=False): sgmllib.SGMLParser.__init__(self) self.hierarchy = [] self.lidata = None self.lidatalist = None self.savedata = None self.title = None self.bugcount = 0 self.mode = mode self.cgi = cgi self.followups = followups self.inbuglist = self.intrailerinfo = False self.bugtitle = None if followups: self.preblock = [] else: self.preblock = '' self.endh2 = False # --- Formatter interface, taking care of 'savedata' mode; # shouldn't need to be overridden def handle_data(self, data): if self.savedata is not None: self.savedata += data # --- Hooks to save data; shouldn't need to be overridden def save_bgn(self): self.savedata = '' def save_end(self, mode=False): data = self.savedata if not mode and data: data = ' '.join(data.split()) self.savedata = None return data def start_h1(self, attrs): self.save_bgn() self.oldmode = self.mode self.mode = 'title' def end_h1(self): self.title = self.save_end() self.mode = self.oldmode def start_h2(self, attrs): if self.lidata: self.check_li() self.save_bgn() def end_h2(self): if self.mode == 'summary': hiertitle = self.save_end() if 'bug' in hiertitle: self.hierarchy.append( (hiertitle, []) ) self.endh2 = True # We are at the end of a title, flag
    
        def start_ul(self, attrs):
            if self.mode == 'summary':
                for k, v in attrs:
                    if k == 'class' and v == 'bugs':
                        self.inbuglist = True
    
        def end_ul(self):
            if self.inbuglist:
                self.check_li()
            
            self.inbuglist = False
    
        def do_br(self, attrs):
            if self.mode == 'title':
                self.savedata = ""
            elif self.mode == 'summary' and self.inbuglist and not self.intrailerinfo:
                self.bugtitle = self.save_end()
                self.intrailerinfo = True
                self.save_bgn()
    
        def check_li(self):
            if self.mode == 'summary':
                if not self.intrailerinfo:
                    self.bugtitle = self.save_end()
                    trailinfo = ''
                else:
                    trailinfo = self.save_end()
    
                match = re.search(r'fixed:\s+([\w.+~-]+(\s+[\w.+~:-]+)?)', trailinfo)
                if match:
                    title = self.bugtitle
                    bits = re.split(r':\s+', title, 1)
                    if len(bits) > 1:
                        buginfo = '%s [FIXED %s]: %s' % (
                            bits[0], match.group(1), bits[1])
                    else:
                        if title.endswith(':'):
                            title = title[:-1]
                        
                        buginfo = '%s [FIXED %s]' % (title, match.group(1))
                else:
                    buginfo = self.bugtitle
                
                self.lidatalist.append(buginfo)
                self.bugcount += 1
    
                self.lidata = self.intrailerinfo = False
    
        def do_li(self, attrs):
            if self.mode == 'summary' and self.inbuglist:
                if self.lidata: self.check_li()
    
                self.lidata = True
                if self.hierarchy:
                    self.lidatalist = self.hierarchy[-1][1]
                else:
                    self.lidatalist = []
                self.save_bgn()
    
        def start_pre(self, attrs):
            "Save 
     when we follow a "
            if self.followups:
                if not self.endh2: return
            else:
                if self.cgi and self.preblock: return
            
            self.save_bgn()
    
        def end_pre(self):
            if self.followups:
                if not self.endh2: return
                self.endh2 = False	# Done with a report, reset .
                stuff = self.save_end(1)
                if not self.cgi:
                    self.preblock.insert(0, stuff)
                else:
                    self.preblock.append(stuff)
            elif not (self.preblock and self.cgi):
                self.preblock = self.save_end(1)
    
        def reorganize(self):
            if not self.hierarchy:
                return
    
            newhierarchy = []
            fixed = []
            fixedfinder = re.compile(r'\[FIXED ([^\]]+)\]')
            resolvedfinder = re.compile(r'Resolved')
    
            for (title, buglist) in self.hierarchy:
                if 'Resolved' in title:
                    newhierarchy.append( (title, buglist) )
                    continue
                
                bugs = []
                for bug in buglist:
                    if fixedfinder.search(bug):
                        fixed.append(bug)
                    else:
                        bugs.append(bug)
                        
                if bugs:
                    title = ' '.join(title.split()[:-2])
                    if len(bugs) != 1:
                        title += ' (%d bugs)' % len(bugs)
                    else:
                        title += ' (1 bug)'
                    
                    newhierarchy.append( (title, bugs) )
    
            if fixed:
                self.hierarchy = [('Bugs fixed in subsequent releases (%d bugs)' % len(fixed), fixed)] + newhierarchy
    
    def parse_html_report(number, url, http_proxy, followups=False, cgi=True):
        page = open_url(url, http_proxy)
        if not page:
            return None
    
        parser = BTSParser(cgi=cgi, followups=followups)
        for line in page:
            parser.feed(line)
        parser.close()
    
        try:
            page.fp._sock.recv = None
        except:
            pass
        page.close()
    
        items = parser.preblock
        title = "#%d: %s" % (number, parser.title)
    
        if not followups:
            items = [items]
    
        output = []
        for stuff in items:
            parts = stuff.split('\n\n')
            match = re.search('^Date: (.*)$', parts[0], re.M | re.I)
            date_submitted = ''
            if match:
                date_submitted = 'Date: %s\n' % match.group(1)
    
            stuff = ('\n\n'.join(parts[1:])).rstrip()
            if not stuff:
                continue
    
            item = date_submitted+stuff+os.linesep
            output.append(item)
    
        if not output:
            return None
    
        return (title, output)
    
    # XXX: Need to handle charsets properly
    def parse_mbox_report(number, url, http_proxy, followups=False):
        page = open_url(url, http_proxy)
        if not page:
            return None
    
        # Make this seekable
        wholefile = cStringIO.StringIO(page.read())
    
        try:
            page.fp._sock.recv = None
        except:
            pass
        page.close()
    
        mbox = mailbox.UnixMailbox(wholefile, msgfactory)
        title = ''
    
        output = []
        for message in mbox:
            if not message:
                pass
            
            subject = message.get('Subject')
            if not title:
                title = subject
    
            date = message.get('Date')
            fromhdr = message.get('From')
    
            body = entry = ''
            for part in message.walk():
                if part.get_content_type() == 'text/plain' and not body:
                    body = part.get_payload(None, True)
    
            if fromhdr:
                entry += 'From: %s%s' % (fromhdr, os.linesep)
    
            if subject and subject != title:
                entry += 'Subject: %s%s' % (subject, os.linesep)
    
            if date:
                entry += 'Date: %s%s' % (date, os.linesep)
    
            if entry:
                entry += os.linesep
    
            entry += body.rstrip('\n') + os.linesep
    
            output.append(entry)
    
        if not output:
            return None
    
        title = "#%d: %s" % (number, title)
        return (title, output)
    
    def get_cgi_reports(package, system='debian', http_proxy='', archived=False,
                        source=False, version=None):
        try:
    	    page = open_url(cgi_package_url(system, package, archived, source,
                                        version=version), http_proxy)
        except NoNetwork:
    	    page = None
        if not page:
            return (0, None, None)
    
        #content = page.read()
        #if 'Maintainer' not in content:
        #    return (0, None, None)
        
        parser = BTSParser(cgi=True)
        for line in page:
            parser.feed(line)
        parser.close()
        try:
            page.fp._sock.recv = None
        except:
            pass
        page.close()
    
        # Reorganize hierarchy to put recently-fixed bugs at top
        parser.reorganize()
    
        data = (parser.bugcount, parser.title, parser.hierarchy)
        del parser
    
        return data
    
    def get_cgi_report(number, system='debian', http_proxy='', archived=False,
                       followups=False):
        number = int(number)
    
        url = cgi_report_url(system, number, archived='no', mbox=True)
        return parse_mbox_report(number, url, http_proxy, followups)
        #return parse_html_report(number, url, http_proxy, followups, cgi=True)
    
    def get_btsroot(system, mirrors=None):
        if mirrors:
            alternates = SYSTEMS[system].get('mirrors')
            for mirror in mirrors:
                if alternates.has_key(mirror):
                    return alternates[mirror]
        return SYSTEMS[system].get('btsroot', '')
    
    def get_reports(package, system='debian', mirrors=None, version=None,
                    http_proxy='', archived=False, source=False):
        if isinstance(package, basestring):
            if SYSTEMS[system].get('cgiroot'):
                result = get_cgi_reports(package, system, http_proxy, archived,
                                         source, version=version)
                if result: return result
    
            url = package_url(system, package, mirrors, source)
            page = open_url(url, http_proxy)
            if not page:
                return (0, None, None)
    
            #content = page.read()
            #if 'Maintainer' not in content:
            #    return (0, None, None)
    
            parser = BTSParser()
            for line in page:
                parser.feed(line)
            parser.close()
            try:
                page.fp._sock.recv = None
            except:
                pass
            page.close()
    
            return parser.bugcount, parser.title, parser.hierarchy
    
        # A list of bug numbers
        this_hierarchy = []
        package = [int(x) for x in package]
        package.sort()
        for bug in package:
            result = get_report(bug, system, mirrors, http_proxy, archived)
            if result:
                title, body = result
                this_hierarchy.append(title)
                #print title
        
        title = "Multiple bug reports"
        bugcount = len(this_hierarchy)
        hierarchy = [('Reports', this_hierarchy)]
    
        return bugcount, title, hierarchy
    
    def get_report(number, system='debian', mirrors=None,
                   http_proxy='', archived=False, followups=False):
    
        try:
                number = int(number)
        except ValueError:
                sys.stderr.write("%s couldn't be convered to integer.\nPlease report a bug" % (number) )
                return False
        if SYSTEMS[system].get('cgiroot'):
            result = get_cgi_report(number, system, http_proxy, archived,
                                    followups)
            if result: return result
            
        url = report_url(system, number, mirrors)
        if not url: return None
    
        return parse_html_report(number, url, http_proxy, followups, cgi=False)
    
    class NullParser(sgmllib.SGMLParser):
        def __init__(self):
            sgmllib.SGMLParser.__init__(self)
    
    if __name__ == '__main__':
        import pprint
    
        data = get_cgi_reports('reportbug')
        pprint.pprint(data)
        time.sleep(1000)
    apt-offline-1.3.1/apt_offline_core/__init__.py0000644000000000000000000000000011350136350016200 0ustar  apt-offline-1.3.1/apt-offline-gui0000755000000000000000000000337711547566433013552 0ustar  #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # apt-offline
    #
    ############################################################################
    #    Copyright (C) 2010 Manish Sinha                                       #
    #    mail@manishsinha.net                                                  #
    #                                                                          #
    #    This program is free software; you can redistribute it and/or modify  #
    #    it under the terms of the GNU General Public License as published by  #
    #    the Free Software Foundation; either version 3 of the License, or     #
    #    (at your option) any later version.                                   #
    #                                                                          #
    #    This program is distributed in the hope that it will be useful,       #
    #    but WITHOUT ANY WARRANTY; without even the implied warranty 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.,                                       #
    #    59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             #
    ############################################################################
    
    import sys
    import os
    from PyQt4 import QtCore, QtGui
    from apt_offline_gui.AptOfflineQtMain import AptOfflineQtMain
    
    if __name__ == "__main__":
        app = QtGui.QApplication(sys.argv)
        myapp = AptOfflineQtMain()
        myapp.show()
        sys.exit(app.exec_())
            
    apt-offline-1.3.1/LICENSE0000644000000000000000000010437411342725567011640 0ustar  
    		    GNU GENERAL PUBLIC LICENSE
    		       Version 3, 29 June 2007
    
     Copyright (C) 2007 Free Software Foundation, Inc. 
     Everyone is permitted to copy and distribute verbatim copies
     of this license document, but changing it is not allowed.
    
    			    Preamble
    
      The GNU General Public License is a free, copyleft license for
    software and other kinds of works.
    
      The licenses for most software and other practical works are designed
    to take away your freedom to share and change the works.  By contrast,
    the GNU General Public License is intended to guarantee your freedom to
    share and change all versions of a program--to make sure it remains free
    software for all its users.  We, the Free Software Foundation, use the
    GNU General Public License for most of our software; it applies also to
    any other work released this way by its authors.  You can apply it to
    your programs, too.
    
      When we speak of free software, we are referring to freedom, not
    price.  Our General Public Licenses are designed to make sure that you
    have the freedom to distribute copies of free software (and charge for
    them if you wish), that you receive source code or can get it if you
    want it, that you can change the software or use pieces of it in new
    free programs, and that you know you can do these things.
    
      To protect your rights, we need to prevent others from denying you
    these rights or asking you to surrender the rights.  Therefore, you have
    certain responsibilities if you distribute copies of the software, or if
    you modify it: responsibilities to respect the freedom of others.
    
      For example, if you distribute copies of such a program, whether
    gratis or for a fee, you must pass on to the recipients the same
    freedoms that you received.  You must make sure that they, too, receive
    or can get the source code.  And you must show them these terms so they
    know their rights.
    
      Developers that use the GNU GPL protect your rights with two steps:
    (1) assert copyright on the software, and (2) offer you this License
    giving you legal permission to copy, distribute and/or modify it.
    
      For the developers' and authors' protection, the GPL clearly explains
    that there is no warranty for this free software.  For both users' and
    authors' sake, the GPL requires that modified versions be marked as
    changed, so that their problems will not be attributed erroneously to
    authors of previous versions.
    
      Some devices are designed to deny users access to install or run
    modified versions of the software inside them, although the manufacturer
    can do so.  This is fundamentally incompatible with the aim of
    protecting users' freedom to change the software.  The systematic
    pattern of such abuse occurs in the area of products for individuals to
    use, which is precisely where it is most unacceptable.  Therefore, we
    have designed this version of the GPL to prohibit the practice for those
    products.  If such problems arise substantially in other domains, we
    stand ready to extend this provision to those domains in future versions
    of the GPL, as needed to protect the freedom of users.
    
      Finally, every program is threatened constantly by software patents.
    States should not allow patents to restrict development and use of
    software on general-purpose computers, but in those that do, we wish to
    avoid the special danger that patents applied to a free program could
    make it effectively proprietary.  To prevent this, the GPL assures that
    patents cannot be used to render the program non-free.
    
      The precise terms and conditions for copying, distribution and
    modification follow.
    
    		       TERMS AND CONDITIONS
    
      0. Definitions.
    
      "This License" refers to version 3 of the GNU General Public License.
    
      "Copyright" also means copyright-like laws that apply to other kinds of
    works, such as semiconductor masks.
     
      "The Program" refers to any copyrightable work licensed under this
    License.  Each licensee is addressed as "you".  "Licensees" and
    "recipients" may be individuals or organizations.
    
      To "modify" a work means to copy from or adapt all or part of the work
    in a fashion requiring copyright permission, other than the making of an
    exact copy.  The resulting work is called a "modified version" of the
    earlier work or a work "based on" the earlier work.
    
      A "covered work" means either the unmodified Program or a work based
    on the Program.
    
      To "propagate" a work means to do anything with it that, without
    permission, would make you directly or secondarily liable for
    infringement under applicable copyright law, except executing it on a
    computer or modifying a private copy.  Propagation includes copying,
    distribution (with or without modification), making available to the
    public, and in some countries other activities as well.
    
      To "convey" a work means any kind of propagation that enables other
    parties to make or receive copies.  Mere interaction with a user through
    a computer network, with no transfer of a copy, is not conveying.
    
      An interactive user interface displays "Appropriate Legal Notices"
    to the extent that it includes a convenient and prominently visible
    feature that (1) displays an appropriate copyright notice, and (2)
    tells the user that there is no warranty for the work (except to the
    extent that warranties are provided), that licensees may convey the
    work under this License, and how to view a copy of this License.  If
    the interface presents a list of user commands or options, such as a
    menu, a prominent item in the list meets this criterion.
    
      1. Source Code.
    
      The "source code" for a work means the preferred form of the work
    for making modifications to it.  "Object code" means any non-source
    form of a work.
    
      A "Standard Interface" means an interface that either is an official
    standard defined by a recognized standards body, or, in the case of
    interfaces specified for a particular programming language, one that
    is widely used among developers working in that language.
    
      The "System Libraries" of an executable work include anything, other
    than the work as a whole, that (a) is included in the normal form of
    packaging a Major Component, but which is not part of that Major
    Component, and (b) serves only to enable use of the work with that
    Major Component, or to implement a Standard Interface for which an
    implementation is available to the public in source code form.  A
    "Major Component", in this context, means a major essential component
    (kernel, window system, and so on) of the specific operating system
    (if any) on which the executable work runs, or a compiler used to
    produce the work, or an object code interpreter used to run it.
    
      The "Corresponding Source" for a work in object code form means all
    the source code needed to generate, install, and (for an executable
    work) run the object code and to modify the work, including scripts to
    control those activities.  However, it does not include the work's
    System Libraries, or general-purpose tools or generally available free
    programs which are used unmodified in performing those activities but
    which are not part of the work.  For example, Corresponding Source
    includes interface definition files associated with source files for
    the work, and the source code for shared libraries and dynamically
    linked subprograms that the work is specifically designed to require,
    such as by intimate data communication or control flow between those
    subprograms and other parts of the work.
    
      The Corresponding Source need not include anything that users
    can regenerate automatically from other parts of the Corresponding
    Source.
    
      The Corresponding Source for a work in source code form is that
    same work.
    
      2. Basic Permissions.
    
      All rights granted under this License are granted for the term of
    copyright on the Program, and are irrevocable provided the stated
    conditions are met.  This License explicitly affirms your unlimited
    permission to run the unmodified Program.  The output from running a
    covered work is covered by this License only if the output, given its
    content, constitutes a covered work.  This License acknowledges your
    rights of fair use or other equivalent, as provided by copyright law.
    
      You may make, run and propagate covered works that you do not
    convey, without conditions so long as your license otherwise remains
    in force.  You may convey covered works to others for the sole purpose
    of having them make modifications exclusively for you, or provide you
    with facilities for running those works, provided that you comply with
    the terms of this License in conveying all material for which you do
    not control copyright.  Those thus making or running the covered works
    for you must do so exclusively on your behalf, under your direction
    and control, on terms that prohibit them from making any copies of
    your copyrighted material outside their relationship with you.
    
      Conveying under any other circumstances is permitted solely under
    the conditions stated below.  Sublicensing is not allowed; section 10
    makes it unnecessary.
    
      3. Protecting Users' Legal Rights From Anti-Circumvention Law.
    
      No covered work shall be deemed part of an effective technological
    measure under any applicable law fulfilling obligations under article
    11 of the WIPO copyright treaty adopted on 20 December 1996, or
    similar laws prohibiting or restricting circumvention of such
    measures.
    
      When you convey a covered work, you waive any legal power to forbid
    circumvention of technological measures to the extent such circumvention
    is effected by exercising rights under this License with respect to
    the covered work, and you disclaim any intention to limit operation or
    modification of the work as a means of enforcing, against the work's
    users, your or third parties' legal rights to forbid circumvention of
    technological measures.
    
      4. Conveying Verbatim Copies.
    
      You may convey verbatim copies of the Program's source code as you
    receive it, in any medium, provided that you conspicuously and
    appropriately publish on each copy an appropriate copyright notice;
    keep intact all notices stating that this License and any
    non-permissive terms added in accord with section 7 apply to the code;
    keep intact all notices of the absence of any warranty; and give all
    recipients a copy of this License along with the Program.
    
      You may charge any price or no price for each copy that you convey,
    and you may offer support or warranty protection for a fee.
    
      5. Conveying Modified Source Versions.
    
      You may convey a work based on the Program, or the modifications to
    produce it from the Program, in the form of source code under the
    terms of section 4, provided that you also meet all of these conditions:
    
        a) The work must carry prominent notices stating that you modified
        it, and giving a relevant date.
    
        b) The work must carry prominent notices stating that it is
        released under this License and any conditions added under section
        7.  This requirement modifies the requirement in section 4 to
        "keep intact all notices".
    
        c) You must license the entire work, as a whole, under this
        License to anyone who comes into possession of a copy.  This
        License will therefore apply, along with any applicable section 7
        additional terms, to the whole of the work, and all its parts,
        regardless of how they are packaged.  This License gives no
        permission to license the work in any other way, but it does not
        invalidate such permission if you have separately received it.
    
        d) If the work has interactive user interfaces, each must display
        Appropriate Legal Notices; however, if the Program has interactive
        interfaces that do not display Appropriate Legal Notices, your
        work need not make them do so.
    
      A compilation of a covered work with other separate and independent
    works, which are not by their nature extensions of the covered work,
    and which are not combined with it such as to form a larger program,
    in or on a volume of a storage or distribution medium, is called an
    "aggregate" if the compilation and its resulting copyright are not
    used to limit the access or legal rights of the compilation's users
    beyond what the individual works permit.  Inclusion of a covered work
    in an aggregate does not cause this License to apply to the other
    parts of the aggregate.
    
      6. Conveying Non-Source Forms.
    
      You may convey a covered work in object code form under the terms
    of sections 4 and 5, provided that you also convey the
    machine-readable Corresponding Source under the terms of this License,
    in one of these ways:
    
        a) Convey the object code in, or embodied in, a physical product
        (including a physical distribution medium), accompanied by the
        Corresponding Source fixed on a durable physical medium
        customarily used for software interchange.
    
        b) Convey the object code in, or embodied in, a physical product
        (including a physical distribution medium), accompanied by a
        written offer, valid for at least three years and valid for as
        long as you offer spare parts or customer support for that product
        model, to give anyone who possesses the object code either (1) a
        copy of the Corresponding Source for all the software in the
        product that is covered by this License, on a durable physical
        medium customarily used for software interchange, for a price no
        more than your reasonable cost of physically performing this
        conveying of source, or (2) access to copy the
        Corresponding Source from a network server at no charge.
    
        c) Convey individual copies of the object code with a copy of the
        written offer to provide the Corresponding Source.  This
        alternative is allowed only occasionally and noncommercially, and
        only if you received the object code with such an offer, in accord
        with subsection 6b.
    
        d) Convey the object code by offering access from a designated
        place (gratis or for a charge), and offer equivalent access to the
        Corresponding Source in the same way through the same place at no
        further charge.  You need not require recipients to copy the
        Corresponding Source along with the object code.  If the place to
        copy the object code is a network server, the Corresponding Source
        may be on a different server (operated by you or a third party)
        that supports equivalent copying facilities, provided you maintain
        clear directions next to the object code saying where to find the
        Corresponding Source.  Regardless of what server hosts the
        Corresponding Source, you remain obligated to ensure that it is
        available for as long as needed to satisfy these requirements.
    
        e) Convey the object code using peer-to-peer transmission, provided
        you inform other peers where the object code and Corresponding
        Source of the work are being offered to the general public at no
        charge under subsection 6d.
    
      A separable portion of the object code, whose source code is excluded
    from the Corresponding Source as a System Library, need not be
    included in conveying the object code work.
    
      A "User Product" is either (1) a "consumer product", which means any
    tangible personal property which is normally used for personal, family,
    or household purposes, or (2) anything designed or sold for incorporation
    into a dwelling.  In determining whether a product is a consumer product,
    doubtful cases shall be resolved in favor of coverage.  For a particular
    product received by a particular user, "normally used" refers to a
    typical or common use of that class of product, regardless of the status
    of the particular user or of the way in which the particular user
    actually uses, or expects or is expected to use, the product.  A product
    is a consumer product regardless of whether the product has substantial
    commercial, industrial or non-consumer uses, unless such uses represent
    the only significant mode of use of the product.
    
      "Installation Information" for a User Product means any methods,
    procedures, authorization keys, or other information required to install
    and execute modified versions of a covered work in that User Product from
    a modified version of its Corresponding Source.  The information must
    suffice to ensure that the continued functioning of the modified object
    code is in no case prevented or interfered with solely because
    modification has been made.
    
      If you convey an object code work under this section in, or with, or
    specifically for use in, a User Product, and the conveying occurs as
    part of a transaction in which the right of possession and use of the
    User Product is transferred to the recipient in perpetuity or for a
    fixed term (regardless of how the transaction is characterized), the
    Corresponding Source conveyed under this section must be accompanied
    by the Installation Information.  But this requirement does not apply
    if neither you nor any third party retains the ability to install
    modified object code on the User Product (for example, the work has
    been installed in ROM).
    
      The requirement to provide Installation Information does not include a
    requirement to continue to provide support service, warranty, or updates
    for a work that has been modified or installed by the recipient, or for
    the User Product in which it has been modified or installed.  Access to a
    network may be denied when the modification itself materially and
    adversely affects the operation of the network or violates the rules and
    protocols for communication across the network.
    
      Corresponding Source conveyed, and Installation Information provided,
    in accord with this section must be in a format that is publicly
    documented (and with an implementation available to the public in
    source code form), and must require no special password or key for
    unpacking, reading or copying.
    
      7. Additional Terms.
    
      "Additional permissions" are terms that supplement the terms of this
    License by making exceptions from one or more of its conditions.
    Additional permissions that are applicable to the entire Program shall
    be treated as though they were included in this License, to the extent
    that they are valid under applicable law.  If additional permissions
    apply only to part of the Program, that part may be used separately
    under those permissions, but the entire Program remains governed by
    this License without regard to the additional permissions.
    
      When you convey a copy of a covered work, you may at your option
    remove any additional permissions from that copy, or from any part of
    it.  (Additional permissions may be written to require their own
    removal in certain cases when you modify the work.)  You may place
    additional permissions on material, added by you to a covered work,
    for which you have or can give appropriate copyright permission.
    
      Notwithstanding any other provision of this License, for material you
    add to a covered work, you may (if authorized by the copyright holders of
    that material) supplement the terms of this License with terms:
    
        a) Disclaiming warranty or limiting liability differently from the
        terms of sections 15 and 16 of this License; or
    
        b) Requiring preservation of specified reasonable legal notices or
        author attributions in that material or in the Appropriate Legal
        Notices displayed by works containing it; or
    
        c) Prohibiting misrepresentation of the origin of that material, or
        requiring that modified versions of such material be marked in
        reasonable ways as different from the original version; or
    
        d) Limiting the use for publicity purposes of names of licensors or
        authors of the material; or
    
        e) Declining to grant rights under trademark law for use of some
        trade names, trademarks, or service marks; or
    
        f) Requiring indemnification of licensors and authors of that
        material by anyone who conveys the material (or modified versions of
        it) with contractual assumptions of liability to the recipient, for
        any liability that these contractual assumptions directly impose on
        those licensors and authors.
    
      All other non-permissive additional terms are considered "further
    restrictions" within the meaning of section 10.  If the Program as you
    received it, or any part of it, contains a notice stating that it is
    governed by this License along with a term that is a further
    restriction, you may remove that term.  If a license document contains
    a further restriction but permits relicensing or conveying under this
    License, you may add to a covered work material governed by the terms
    of that license document, provided that the further restriction does
    not survive such relicensing or conveying.
    
      If you add terms to a covered work in accord with this section, you
    must place, in the relevant source files, a statement of the
    additional terms that apply to those files, or a notice indicating
    where to find the applicable terms.
    
      Additional terms, permissive or non-permissive, may be stated in the
    form of a separately written license, or stated as exceptions;
    the above requirements apply either way.
    
      8. Termination.
    
      You may not propagate or modify a covered work except as expressly
    provided under this License.  Any attempt otherwise to propagate or
    modify it is void, and will automatically terminate your rights under
    this License (including any patent licenses granted under the third
    paragraph of section 11).
    
      However, if you cease all violation of this License, then your
    license from a particular copyright holder is reinstated (a)
    provisionally, unless and until the copyright holder explicitly and
    finally terminates your license, and (b) permanently, if the copyright
    holder fails to notify you of the violation by some reasonable means
    prior to 60 days after the cessation.
    
      Moreover, your license from a particular copyright holder is
    reinstated permanently if the copyright holder notifies you of the
    violation by some reasonable means, this is the first time you have
    received notice of violation of this License (for any work) from that
    copyright holder, and you cure the violation prior to 30 days after
    your receipt of the notice.
    
      Termination of your rights under this section does not terminate the
    licenses of parties who have received copies or rights from you under
    this License.  If your rights have been terminated and not permanently
    reinstated, you do not qualify to receive new licenses for the same
    material under section 10.
    
      9. Acceptance Not Required for Having Copies.
    
      You are not required to accept this License in order to receive or
    run a copy of the Program.  Ancillary propagation of a covered work
    occurring solely as a consequence of using peer-to-peer transmission
    to receive a copy likewise does not require acceptance.  However,
    nothing other than this License grants you permission to propagate or
    modify any covered work.  These actions infringe copyright if you do
    not accept this License.  Therefore, by modifying or propagating a
    covered work, you indicate your acceptance of this License to do so.
    
      10. Automatic Licensing of Downstream Recipients.
    
      Each time you convey a covered work, the recipient automatically
    receives a license from the original licensors, to run, modify and
    propagate that work, subject to this License.  You are not responsible
    for enforcing compliance by third parties with this License.
    
      An "entity transaction" is a transaction transferring control of an
    organization, or substantially all assets of one, or subdividing an
    organization, or merging organizations.  If propagation of a covered
    work results from an entity transaction, each party to that
    transaction who receives a copy of the work also receives whatever
    licenses to the work the party's predecessor in interest had or could
    give under the previous paragraph, plus a right to possession of the
    Corresponding Source of the work from the predecessor in interest, if
    the predecessor has it or can get it with reasonable efforts.
    
      You may not impose any further restrictions on the exercise of the
    rights granted or affirmed under this License.  For example, you may
    not impose a license fee, royalty, or other charge for exercise of
    rights granted under this License, and you may not initiate litigation
    (including a cross-claim or counterclaim in a lawsuit) alleging that
    any patent claim is infringed by making, using, selling, offering for
    sale, or importing the Program or any portion of it.
    
      11. Patents.
    
      A "contributor" is a copyright holder who authorizes use under this
    License of the Program or a work on which the Program is based.  The
    work thus licensed is called the contributor's "contributor version".
    
      A contributor's "essential patent claims" are all patent claims
    owned or controlled by the contributor, whether already acquired or
    hereafter acquired, that would be infringed by some manner, permitted
    by this License, of making, using, or selling its contributor version,
    but do not include claims that would be infringed only as a
    consequence of further modification of the contributor version.  For
    purposes of this definition, "control" includes the right to grant
    patent sublicenses in a manner consistent with the requirements of
    this License.
    
      Each contributor grants you a non-exclusive, worldwide, royalty-free
    patent license under the contributor's essential patent claims, to
    make, use, sell, offer for sale, import and otherwise run, modify and
    propagate the contents of its contributor version.
    
      In the following three paragraphs, a "patent license" is any express
    agreement or commitment, however denominated, not to enforce a patent
    (such as an express permission to practice a patent or covenant not to
    sue for patent infringement).  To "grant" such a patent license to a
    party means to make such an agreement or commitment not to enforce a
    patent against the party.
    
      If you convey a covered work, knowingly relying on a patent license,
    and the Corresponding Source of the work is not available for anyone
    to copy, free of charge and under the terms of this License, through a
    publicly available network server or other readily accessible means,
    then you must either (1) cause the Corresponding Source to be so
    available, or (2) arrange to deprive yourself of the benefit of the
    patent license for this particular work, or (3) arrange, in a manner
    consistent with the requirements of this License, to extend the patent
    license to downstream recipients.  "Knowingly relying" means you have
    actual knowledge that, but for the patent license, your conveying the
    covered work in a country, or your recipient's use of the covered work
    in a country, would infringe one or more identifiable patents in that
    country that you have reason to believe are valid.
      
      If, pursuant to or in connection with a single transaction or
    arrangement, you convey, or propagate by procuring conveyance of, a
    covered work, and grant a patent license to some of the parties
    receiving the covered work authorizing them to use, propagate, modify
    or convey a specific copy of the covered work, then the patent license
    you grant is automatically extended to all recipients of the covered
    work and works based on it.
    
      A patent license is "discriminatory" if it does not include within
    the scope of its coverage, prohibits the exercise of, or is
    conditioned on the non-exercise of one or more of the rights that are
    specifically granted under this License.  You may not convey a covered
    work if you are a party to an arrangement with a third party that is
    in the business of distributing software, under which you make payment
    to the third party based on the extent of your activity of conveying
    the work, and under which the third party grants, to any of the
    parties who would receive the covered work from you, a discriminatory
    patent license (a) in connection with copies of the covered work
    conveyed by you (or copies made from those copies), or (b) primarily
    for and in connection with specific products or compilations that
    contain the covered work, unless you entered into that arrangement,
    or that patent license was granted, prior to 28 March 2007.
    
      Nothing in this License shall be construed as excluding or limiting
    any implied license or other defenses to infringement that may
    otherwise be available to you under applicable patent law.
    
      12. No Surrender of Others' Freedom.
    
      If conditions are imposed on you (whether by court order, agreement or
    otherwise) that contradict the conditions of this License, they do not
    excuse you from the conditions of this License.  If you cannot convey a
    covered work so as to satisfy simultaneously your obligations under this
    License and any other pertinent obligations, then as a consequence you may
    not convey it at all.  For example, if you agree to terms that obligate you
    to collect a royalty for further conveying from those to whom you convey
    the Program, the only way you could satisfy both those terms and this
    License would be to refrain entirely from conveying the Program.
    
      13. Use with the GNU Affero General Public License.
    
      Notwithstanding any other provision of this License, you have
    permission to link or combine any covered work with a work licensed
    under version 3 of the GNU Affero General Public License into a single
    combined work, and to convey the resulting work.  The terms of this
    License will continue to apply to the part which is the covered work,
    but the special requirements of the GNU Affero General Public License,
    section 13, concerning interaction through a network will apply to the
    combination as such.
    
      14. Revised Versions of this License.
    
      The Free Software Foundation may publish revised and/or new versions of
    the GNU General Public License from time to time.  Such new versions will
    be similar in spirit to the present version, but may differ in detail to
    address new problems or concerns.
    
      Each version is given a distinguishing version number.  If the
    Program specifies that a certain numbered version of the GNU General
    Public License "or any later version" applies to it, you have the
    option of following the terms and conditions either of that numbered
    version or of any later version published by the Free Software
    Foundation.  If the Program does not specify a version number of the
    GNU General Public License, you may choose any version ever published
    by the Free Software Foundation.
    
      If the Program specifies that a proxy can decide which future
    versions of the GNU General Public License can be used, that proxy's
    public statement of acceptance of a version permanently authorizes you
    to choose that version for the Program.
    
      Later license versions may give you additional or different
    permissions.  However, no additional obligations are imposed on any
    author or copyright holder as a result of your choosing to follow a
    later version.
    
      15. Disclaimer of Warranty.
    
      THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
    APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
    HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
    OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
    THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
    IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
    ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
    
      16. Limitation of Liability.
    
      IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
    WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
    THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
    GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
    USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
    DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
    PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
    EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
    SUCH DAMAGES.
    
      17. Interpretation of Sections 15 and 16.
    
      If the disclaimer of warranty and limitation of liability provided
    above cannot be given local legal effect according to their terms,
    reviewing courts shall apply local law that most closely approximates
    an absolute waiver of all civil liability in connection with the
    Program, unless a warranty or assumption of liability accompanies a
    copy of the Program in return for a fee.
    
    		     END OF TERMS AND CONDITIONS
    
    	    How to Apply These Terms to Your New Programs
    
      If you develop a new program, and you want it to be of the greatest
    possible use to the public, the best way to achieve this is to make it
    free software which everyone can redistribute and change under these terms.
    
      To do so, attach the following notices to the program.  It is safest
    to attach them to the start of each source file to most effectively
    state the exclusion of warranty; and each file should have at least
    the "copyright" line and a pointer to where the full notice is found.
    
        
        Copyright (C)   
    
        This program is free software: you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation, either version 3 of the License, or
        (at your option) any later version.
    
        This program is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty 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, see .
    
    Also add information on how to contact you by electronic and paper mail.
    
      If the program does terminal interaction, make it output a short
    notice like this when it starts in an interactive mode:
    
          Copyright (C)   
        This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
        This is free software, and you are welcome to redistribute it
        under certain conditions; type `show c' for details.
    
    The hypothetical commands `show w' and `show c' should show the appropriate
    parts of the General Public License.  Of course, your program's commands
    might be different; for a GUI interface, you would use an "about box".
    
      You should also get your employer (if you work as a programmer) or school,
    if any, to sign a "copyright disclaimer" for the program, if necessary.
    For more information on this, and how to apply and follow the GNU GPL, see
    .
    
      The GNU General Public License does not permit incorporating your program
    into proprietary programs.  If your program is a subroutine library, you
    may consider it more useful to permit linking proprietary applications with
    the library.  If this is what you want to do, use the GNU Lesser General
    Public License instead of this License.  But first, please read
    .
    
    apt-offline-1.3.1/README0000644000000000000000000000174511465561546011512 0ustar  apt-offline -- An Offline Package Manager 
    (C) 2005 - 2010 Ritesh Raj Sarraf  
    
    
    apt-offline is an offline package management tool written in the Python Programming Language.
    This program, as of now, is intended for people using Debian (And Debian based) systems. 
    
    This program allows leveraging the power of Debian (more precisely apt-get) onto a completely
    disconnected machine. Most of the people with slow or no internet connection (most of those in
    India/Nepal/Pakistan and nearby countries) have not considered using Debian (or Debian derived
    distributions) because Debian's real taste is experienced when it is connected to the internet.
    
    This utility is an attempt in making that problem eradicate. I hope this utility comes to use to you.
    I'd be eager to hear your comments/suggestions. Feel free to drop an email at rrs _AT_ researchut |DOT| com
    
    
    
    Dedication
    ----------
    This software is dedicated to the memory of my father Santosh Kumar Sarraf. We miss you a lot.
    apt-offline-1.3.1/release_setps0000644000000000000000000000031511346234237013373 0ustar  1. Complete you all code changes and test properly
    2. Bump version in AptOfflineCore.py
    3. Execute make "html" target to create html docs from the manpage
    4. Perhaps now, go and do a `git tag -s rel_name`
    apt-offline-1.3.1/.pydevproject0000644000000000000000000000064112074206154013327 0ustar  
    
    
    
    
    /apt-offline
    
    python 2.7
    Default
    
    apt-offline-1.3.1/.settings/0000755000000000000000000000000012067271677012543 5ustar  apt-offline-1.3.1/.settings/org.eclipse.core.resources.prefs0000644000000000000000000000060012103415024020723 0ustar  eclipse.preferences.version=1
    encoding//apt_offline_gui/AptOfflineQtFetch.py=utf-8
    encoding//apt_offline_gui/AptOfflineQtInstall.py=utf-8
    encoding//apt_offline_gui/AptOfflineQtInstallBugList.py=utf-8
    encoding//apt_offline_gui/UiDataStructs.py=utf-8
    encoding//apt_offline_gui/Ui_AptOfflineQtAbout.py=utf-8
    encoding//apt_offline_gui/resources_rc.py=utf-8
    encoding/apt-offline-gui=utf-8
    apt-offline-1.3.1/debian/0000755000000000000000000000000012157275216012040 5ustar  apt-offline-1.3.1/debian/apt-offline.install0000644000000000000000000000011712157275151015631 0ustar  apt-offline	/usr/bin/
    apt_offline_core/*	/usr/share/pyshared/apt_offline_core/
    apt-offline-1.3.1/debian/compat0000644000000000000000000000000212157275151013234 0ustar  7
    apt-offline-1.3.1/debian/manpages0000644000000000000000000000001612157275151013551 0ustar  apt-offline.8
    apt-offline-1.3.1/debian/source/0000755000000000000000000000000012157275151013336 5ustar  apt-offline-1.3.1/debian/source/format0000644000000000000000000000001512157275151014545 0ustar  3.0 (native)
    apt-offline-1.3.1/debian/apt-offline-gui.install0000644000000000000000000000022112157275151016407 0ustar  apt-offline-gui		/usr/bin/
    apt_offline_gui/icons/*	/usr/share/pixmaps/apt-offline-gui/
    apt_offline_gui/*.py	/usr/share/pyshared/apt_offline_gui/
    apt-offline-1.3.1/debian/rules0000755000000000000000000000005512157275151013116 0ustar  #!/usr/bin/make -f
    %:
    	dh $@ --with python2
    
    apt-offline-1.3.1/debian/README.Debian0000644000000000000000000000040412157275151014075 0ustar  apt-offline for Debian
    ----------------------
    
    apt-offline was designed for distributions using APT (Advanced Packaging Tool)
    and thus should work with distributions like Ubuntu also
    
     -- Ritesh Raj Sarraf   Wed, 23 Sep 2009 13:33:26 +0530
    apt-offline-1.3.1/debian/changelog0000644000000000000000000001252612157275216013720 0ustar  apt-offline (1.3.1) unstable; urgency=low
    
      * Upload to unstable
    
     -- Ritesh Raj Sarraf   Sun, 16 Jun 2013 14:12:34 +0530
    
    apt-offline (1.3) experimental; urgency=low
    
      * New GUI Feature Release
      * [320714a] Hard code the license in case it is not found by us
      * [2a0be56] Add GUI Window support to browse through bug reports
      * [8eadbd9] Add support for downloads to directory also
    
     -- Ritesh Raj Sarraf   Sat, 12 Jan 2013 12:25:25 +0530
    
    apt-offline (1.2) unstable; urgency=low
    
      * New Bugfix Release
      * [6b6c051] Remove non-existent unlink.
        Thanks to Michael Druckenmiller
      * [d98bd6b] Update manpage and correct argument positions.
        Thanks to Pawel Sadkowski (Closes: #647214)
      * [035a0b6] Fix gpg signature verfication (Closes: #664654)
      * [c42b435] Bump Standards Version to 3.9.3. No changes required.
      * [cb9140d] Fix typos in package description.
        Thanks to Erik Esterer (Closes: #644487)
    
     -- Ritesh Raj Sarraf   Wed, 25 Apr 2012 19:05:58 +0530
    
    apt-offline (1.1.1) unstable; urgency=low
    
      * [ad257b7] Switch to dh_python2.
        Thanks to Julian Taylor (Closes: 631419)
      * [c09d819] Manpage spell-check fixes.
        Thanks to A. Costa (Closes: 624745)
      * [d871f89] Update Standards Version to 3.9.2. No changes required.
    
     -- Ritesh Raj Sarraf   Mon, 01 Aug 2011 14:41:15 +0530
    
    apt-offline (1.1) unstable; urgency=low
    
      * New Bugfix Release (Closes: #593690, #615976) 
        + Adds new feature for basic proxy support 
    
     -- Ritesh Raj Sarraf   Fri, 08 Apr 2011 16:47:12 +0530
    
    apt-offline (1.0.2) unstable; urgency=low
    
      * cherry pick b35ef11d1d4b282f (Closes: #613617)
        Thanks to Julian Taylor for the report
    
     -- Ritesh Raj Sarraf   Tue, 08 Mar 2011 14:14:17 +0530
    
    apt-offline (1.0.1) unstable; urgency=low
    
      * Reupload to sid 
    
     -- Ritesh Raj Sarraf   Thu, 10 Feb 2011 12:14:56 +0530
    
    apt-offline (1.0) experimental; urgency=low
    
      * New Feature Release
        + This release bring a new GUI tool for apt-offline. Kudos to
          Abhishek Mishra and Manish Sinha
      * Introduce the new apt-offline-gui package
      * Bump Standards Version to 3.9.1 (No changes required) 
    
     -- Ritesh Raj Sarraf   Sun, 07 Nov 2010 22:08:54 +0530
    
    apt-offline (0.9.9squeeze3) unstable; urgency=low
    
      * Fix checksum error for MD5 checksums 
    
     -- Ritesh Raj Sarraf   Tue, 19 Oct 2010 19:16:13 +0530
    
    apt-offline (0.9.9squeeze2) unstable; urgency=low
    
      * Clean up the cruft that got carried into the source file
    
     -- Ritesh Raj Sarraf   Fri, 03 Sep 2010 22:48:21 +0530
    
    apt-offline (0.9.9squeeze1) unstable; urgency=low
    
      * Add less to Depends because we depend on `less` to display the
        bug reports
      * Check if argument is a directory. (Closes: #588908) 
    
     -- Ritesh Raj Sarraf   Fri, 20 Aug 2010 17:27:02 +0530
    
    apt-offline (0.9.9) unstable; urgency=low
    
      * New Feature Release
        + Add support to cycle through all supported Packages.extension types
          (Closes: #583502)
        + Fix exception which downloading bug reports (Closes: #584427) 
    
     -- Ritesh Raj Sarraf   Sat, 19 Jun 2010 12:18:20 +0530
    
    apt-offline (0.9.8) unstable; urgency=low
    
      * New Bug Fix Release
      * Change address to my official Debian address and remove the DMUA flag
      * Switch to 3.0 (native) source format 
    
     -- Ritesh Raj Sarraf   Mon, 10 May 2010 21:49:35 +0530
    
    apt-offline (0.9.7) unstable; urgency=low
    
      * New Bug Fix Release
        + Allow release type to be specified when in the set operation, for
          an upgrade. (This is the equivalent of `apt-get -t unstable upgrade`) 
        + Add new option --simulate (which replaces the old --test-windows option)
        + Do a check to ensure that the argument to the 'get' operation is a file
        + Fix APT/GPG security issues. When just running the 'set' operation, the
          apt gpg database gets invalid. Currently, fixed with a workaround. For
          more details, see DBTS: 565918
          Many thanks to Paul Nijjar for reporting the bug
      * Update Standards Version to 3.8.4 (No changes required) 
    
     -- Ritesh Raj Sarraf   Wed, 17 Mar 2010 17:10:41 +0530
    
    apt-offline (0.9.6.1) unstable; urgency=low
    
      [ Ritesh Raj Sarraf ]
      * New Bug Fix Release
        + Show bug numbers correctly 
        + Don't hardcode checksum type during checks
        + Check for APT's partial directory
        + Carefully update APT's Package cache data
        + Discard damaged downloads
        + Fix ZipFile.read()
        + Don't report download failure for Release.gpg files
        + Add html doc for the man page
      * Add DM-Upload field 
    
      [ Y Giridhar Appaji Nag ]
      * Change Debian package version to 0.9.6.1 for Dm-Upload-Allowed: yes
    
     -- Y Giridhar Appaji Nag   Mon, 01 Mar 2010 16:05:40 +0530
    
    apt-offline (0.9.5) unstable; urgency=low
    
      * New Feature Release
        + Add support for source packages along with their Build Dependencies
        + Correct display count of remaining items to be downloaded
        + Many more bugfixes
      * Update package description. Thanks to Imran Ebrahim. (Closes: #550939)
    
     -- Ritesh Raj Sarraf   Fri, 30 Oct 2009 12:54:59 +0530
    
    apt-offline (0.9.4) unstable; urgency=low
    
      * Initial release (Closes: #531699)
      * Dedicated to my father Santosh Kumar Sarraf. We miss you Dad.
    
     -- Ritesh Raj Sarraf   Tue, 13 Oct 2009 16:19:21 +0530
    apt-offline-1.3.1/debian/control0000644000000000000000000000440312157275151013442 0ustar  Source: apt-offline
    Section: admin
    Priority: optional
    Maintainer: Ritesh Raj Sarraf 
    Build-Depends: debhelper (>= 7), python (>= 2.6.6-3~)
    Standards-Version: 3.9.3
    X-Python-Version: >= 2.5
    Homepage: http://apt-offline.alioth.debian.org
    Vcs-Git: git://git.debian.org/apt-offline/apt-offline.git
    Vcs-Browser: http://git.debian.org/?p=apt-offline/apt-offline.git
    
    Package: apt-offline
    Architecture: all
    Depends: ${misc:Depends}, ${python:Depends}, python-argparse, python, apt, less
    Description: offline APT package manager
     apt-offline is an Offline APT Package Manager.
     .
     apt-offline can fully update and upgrade an APT based distribution without
     connecting to the network, all of it transparent to APT.
     .
     apt-offline can be used to generate a signature on a machine (with no network).
     This signature contains all download information required for the APT database
     system. This signature file can be used on another machine connected to the
     internet (which need not be a Debian box and can even be running windows) to
     download the updates.
     The downloaded data will contain all updates in a format understood by APT and
     this data can be used by apt-offline to update the non-networked machine.
     .
     apt-offline can also fetch bug reports and make them available offline.
    
    Package: apt-offline-gui
    Architecture: all
    Depends: ${misc:Depends}, ${python:Depends}, apt-offline (= ${binary:Version}),
     python-qt4
    Description: offline APT package manager - GUI
     apt-offline is an Offline APT Package Manager.
     .
     apt-offline can fully update and upgrade an APT based distribution without
     connecting to the network, all of it transparent to APT.
     .
     apt-offline can be used to generate a signature on a machine (with no network).
     This signature contains all download information required for the APT database
     system. This signature file can be used on another machine connected to the
     internet (which need not be a Debian box and can even be running windows) to
     download the updates.
     The downloaded data will contain all updates in a format understood by APT and
     this data can be used by apt-offline to update the non-networked machine.
     .
     apt-offline can also fetch bug reports and make them available offline.
     .
     This package contains the graphical user interface to apt-offline.
    apt-offline-1.3.1/debian/copyright0000644000000000000000000000573112157275151013777 0ustar  This work was packaged for Debian by:
    
        Ritesh Raj Sarraf  on Wed, 23 Sep 2009 13:33:26 +0530
    
    It was downloaded from http://apt-offline.alioth.debian.org
    
    Upstream Author:
    
        Ritesh Raj Sarraf 
    
    Copyright:
    
        Copyright (C) 2005-2009 Ritesh Raj Sarraf 
        Copyright (C) 2006-2009 Steven J. Bethard 
        Copyright (C) 1999-2006 Chris Lawrence 
        Copyright (C) 2000 Jason Petrone 
    
    License:
    
        This package is free software; you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation; either version 3 of the License, or
        (at your option) any later version.
    
        This package is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty 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 package; if not, write to the Free Software
        Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
    
    
    The Debian packaging is:
    
        Copyright (C) 2009 Ritesh Raj Sarraf 
        and is licensed under the GPL version 3 or later, 
        see `/usr/share/common-licenses/GPL-3'.
    
    AptOffline_argparse.py is license under:
        Licensed under the Apache License, Version 2.0 (the "License"); you may not
        use this file except in compliance with the License. You may obtain a copy
        of the License at
    
        http://www.apache.org/licenses/LICENSE-2.0
    
        Unless required by applicable law or agreed to in writing, software
        distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
        WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
        License for the specific language governing permissions and limitations
        under the License.
    
        On Debian, the full license can be seen at
        `/usr/share/common-licenses/Apache-2.0`
    
    AptOfflineDebianBtsLib.py, AptOffline_urlutils.py and AptOffline_reportbug_exceptions.py
    are licensed under the following license:
        
        Permission to use, copy, modify, and distribute this software and its
        documentation for any purpose and without fee is hereby granted,
        provided that the above copyright notice appears in all copies and that
        both that copyright notice and this permission notice appear in
        supporting documentation.
    
        I DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
        IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL I
        BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
        DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
        WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
        ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
        SOFTWARE.
    apt-offline-1.3.1/debian/docs0000644000000000000000000000005112157275151012705 0ustar  README
    debian/changelog
    apt-offline.html
    apt-offline-1.3.1/debian/apt-offline.doc-base0000644000000000000000000000044112157275151015640 0ustar  Document: apt-offline
    Title: apt-offline online manpage
    Author: Ritesh Raj Sarraf
    Abstract: This set of document provides the manual in html format
    Section: System/Package Management
    
    Format: HTML
    Index: /usr/share/doc/apt-offline/apt-offline.html
    Files: /usr/share/doc/apt-offline/*.html
    apt-offline-1.3.1/.project0000644000000000000000000000055512074206154012263 0ustar  
    
    	apt-offline
    	
    	
    	
    	
    		
    			org.python.pydev.PyDevBuilder
    			
    			
    		
    	
    	
    		org.python.pydev.pythonNature
    	
    
    apt-offline-1.3.1/INSTALL0000644000000000000000000000047111342725567011655 0ustar  ##  Under Linux
    
    # To build
    python setup.py build
    
    # To Install (You might need to be root)
    python setup.py install
    
    ## Under Microsoft Windows
    
    # Execute the file using the python interpreter
    # Assuming python.exe is in your path
    
    # To build
    C:\> python setup.py build
    
    # To install
    C:\> python setup.py install
    apt-offline-1.3.1/apt-offline0000755000000000000000000000307211465207654012754 0ustar  #!/usr/bin/env python
    # apt-offline
    #
    ############################################################################
    #    Copyright (C) 2005, 2010 Ritesh Raj Sarraf                            #
    #    rrs@researchut.com                                                    #
    #                                                                          #
    #    This program is free software; you can redistribute it and/or modify  #
    #    it under the terms of the GNU General Public License as published by  #
    #    the Free Software Foundation; either version 3 of the License, or     #
    #    (at your option) any later version.                                   #
    #                                                                          #
    #    This program is distributed in the hope that it will be useful,       #
    #    but WITHOUT ANY WARRANTY; without even the implied warranty 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.,                                       #
    #    59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             #
    ############################################################################
    
    
    from apt_offline_core.AptOfflineCoreLib import main
    
    if __name__ == "__main__":
        main()
    apt-offline-1.3.1/apt-offline-testcase.bat0000644000000000000000000000164712157275151015331 0ustar  @echo pypt-offline Test Cases
    
    python.exe apt-offline --fetch-update C:\update.uris --threads 5 --zip -d C:\test
    rmdir /q /s C:\test
    ping -n 10 localhost 1>null
    
    python.exe apt-offline --fetch-update C:\update.uris --threads 5 --zip
    rmdir /q /s pypt-downloads
    ping -n 10 localhost 1>null
    
    python.exe apt-offline --fetch-update C:\update.uris --threads 5
    rmdir /q /s pypt-downloads
    ping -n 10 localhost 1>null
    
    python.exe apt-offline --fetch-update C:\update.uris --zip
    rmdir /q /s pypt-downloads
    ping -n 10 localhost 1>null
    
    python.exe apt-offline --fetch-update C:\update.uris --threads 5 -d C:\test
    rmdir /q /s C:\test
    ping -n 10 localhost 1>null
    
    python.exe apt-offline --fetch-upgrade C:\upgrade.uris --threads 5 --zip -d C:\test
    rmdir /q /s C:\test
    ping -n 10 localhost 1>null
    
    python.exe apt-offline --fetch-upgrade C:\upgrade.uris --threads 5 --zip -d C:\test --fetch-bug-reports
    rmdir /q /s C:\test
    ping -n 10 localhost 1>null
    apt-offline-1.3.1/setup.py0000644000000000000000000000426711465541222012333 0ustar  #!/usr/bin/env python
    
    # -*- coding: utf-8 -*-
    
    # Copyright 2009 Ritesh Raj Sarraf 
    #
    # Licensed under the GNU General Public License v3 and later
    
    import textwrap
    import distutils.core
    
    distutils.core.setup(
        name='apt-offline',
        version='0.9.9',
        author='Ritesh Raj Sarraf',
        author_email='rrs@researchut.com',
        url='http://apt-offline.alioth.debian.org',
        #packages = [ 'apt_offline_core' ],
        description='Offline APT Package Manager',
        long_description = textwrap.dedent("""\
            apt-offline is an Offline APT Package Manager
            for Debian and derivatives
            """),
        license='GPL',
        platforms = 'Posix; MacOS X; Windows',
        classifiers=[
            'Development Status :: 3 - Testing/Beta',
            'Environment :: Console',
            'Intended Audience :: End Users',
            'License :: OSI Approved :: GPL',
            'Operating System :: OS Independent',
            'Programming Language :: Python',
            'Topic :: Package Management',
        ],
        py_modules=['apt_offline_core.AptOfflineCoreLib',
                'apt_offline_core.AptOfflineDebianBtsLib',
    #            'apt_offline_core.AptOfflineFetchBugs.py',
                'apt_offline_core.AptOfflineLib',
                'apt_offline_core.AptOfflineMagicLib',
                'apt_offline_core.AptOffline_reportbug_exceptions',
                'apt_offline_core.AptOffline_argparse',
                'apt_offline_core.AptOffline_urlutils',
                'apt_offline_gui.AptOfflineQtAbout',
    	    'apt_offline_gui.AptOfflineQtCommon',
    	    'apt_offline_gui.AptOfflineQtCreateProfile',
    	    'apt_offline_gui.AptOfflineQtFetch',
    	    'apt_offline_gui.AptOfflineQtInstall',
    	    'apt_offline_gui.AptOfflineQtMain',
    	    'apt_offline_gui.AptOfflineQtSaveZip',
    	    'apt_offline_gui.__init__',
    	    'apt_offline_gui.QtProgressBar',
    	    'apt_offline_gui.resources_rc',
    	    'apt_offline_gui.Ui_AptOfflineQtAbout',
    	    'apt_offline_gui.Ui_AptOfflineQtCreateProfile',
    	    'apt_offline_gui.Ui_AptOfflineQtFetch',
    	    'apt_offline_gui.Ui_AptOfflineQtInstall',
    	    'apt_offline_gui.Ui_AptOfflineQtMain',
    	    'apt_offline_gui.Ui_AptOfflineQtSaveZip',
    	    'apt_offline_gui.UiDataStructs',],
        scripts=['apt-offline', 'apt-offline-gui'],
    )
    apt-offline-1.3.1/apt_offline_gui/0000755000000000000000000000000012157275265013754 5ustar  apt-offline-1.3.1/apt_offline_gui/AptOfflineQtCreateProfile.ui0000644000000000000000000002550211547566433021322 0ustar  
    
     CreateProfile
     
      
       Qt::ApplicationModal
      
      
       
        0
        0
        443
        374
       
      
      
       
        0
        0
       
      
      
       
        443
        374
       
      
      
       
        443
        374
       
      
      
       Generate Signature
      
      
       
        
         10
         10
         427
         321
        
       
       
        
         
          
           QLayout::SetFixedSize
          
          
           
            
             
              
               6
              
              
               QLayout::SetMinimumSize
              
              
               
                
                 
                  16777215
                  16777215
                 
                
                
                 Installation Type
                
                
                 Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
                
               
              
             
            
            
             
              
               
                
                 Update
                
               
              
              
               
                
                 Upgrade Packages
                
               
              
              
               
                
                 Install Packages
                
               
              
             
            
            
             
              
               Qt::Horizontal
              
              
               QSizePolicy::Expanding
              
              
               
                20
                20
               
              
             
            
           
          
         
        
        
         
          
           Qt::Vertical
          
          
           QSizePolicy::Fixed
          
          
           
            20
            4
           
          
         
        
        
         
          
           QLayout::SetDefaultConstraint
          
          
           
            
             <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    <html><head><meta name="qrichtext" content="1" /><style type="text/css">
    p, li { white-space: pre-wrap; }
    </style></head><body style=" font-family:'DejaVu Sans'; font-size:8pt; font-weight:400; font-style:normal;">
    <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Install these packages</p>
    <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:7pt;">separate by comma</span></p></body></html>
            
           
          
          
           
            
             false
            
            
             
              0
              30
             
            
            
             false
            
           
          
         
        
        
         
          
           Qt::Vertical
          
          
           QSizePolicy::Fixed
          
          
           
            20
            4
           
          
         
        
        
         
          
           6
          
          
           QLayout::SetDefaultConstraint
          
          
           
            
             
              
               
                
                 Save Signature As 
                
               
              
              
               
                
                 true
                
                
                 
                  0
                  0
                 
                
                
                 
                  0
                  30
                 
                
               
              
              
               
                
                 
                  0
                  30
                 
                
                
                 Browse
                
               
              
             
            
           
          
          
           
            
             
              
               Console Output:
              
             
            
            
             
              
               
                0
                0
               
              
              
               
                0
                100
               
              
              
               
                16777215
                100
               
              
             
            
           
          
         
        
       
      
      
       
        
         25
         325
         401
         40
        
       
       
        
         
          
           Qt::Horizontal
          
          
           
            40
            20
           
          
         
        
        
         
          
           true
          
          
           
            100
            30
           
          
          
           Create
          
          
           
            :/icons/icons/dialog-ok-apply.png:/icons/icons/dialog-ok-apply.png
          
         
        
        
         
          
           Qt::Horizontal
          
          
           
            40
            20
           
          
         
        
        
         
          
           
            100
            30
           
          
          
           Cancel
          
          
           
            :/icons/icons/dialog-cancel.png:/icons/icons/dialog-cancel.png
          
         
        
        
         
          
           Qt::Horizontal
          
          
           
            40
            20
           
          
         
        
       
      
     
     
      
     
     
    
    apt-offline-1.3.1/apt_offline_gui/resources_rc.py0000644000000000000000000017540011465541222017021 0ustar  # -*- coding: utf-8 -*-
    
    # Resource object code
    #
    # Created: Fri Feb 5 02:49:34 2010
    #      by: The Resource Compiler for PyQt (Qt v4.5.0)
    #
    # WARNING! All changes made in this file will be lost!
    
    from PyQt4 import QtCore
    
    qt_resource_data = "\
    \x00\x00\x05\xd3\
    \x89\
    \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
    \x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\
    \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
    \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x03\x76\x00\x00\x03\x76\
    \x01\x7d\xd5\x82\xcc\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
    \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
    \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x05\x50\x49\x44\
    \x41\x54\x78\xda\xc5\x97\x5f\x88\x15\x55\x1c\xc7\x3f\xbf\x73\x66\
    \xee\xde\x7b\x5d\xd7\x5d\x5b\x35\xff\x45\x54\x2c\x6a\xe2\xa2\x54\
    \x94\xe0\x1a\x66\x7f\x48\x5f\xa5\xd6\x8a\x08\x7a\xe9\x21\x7a\x10\
    \x41\x02\x59\xf6\x25\x0a\xf1\x25\x08\x21\xe9\x21\x4c\x45\xa4\x42\
    \x50\xe8\xa1\xa4\x56\x28\x54\xd4\x14\x31\xdb\x20\x6b\xb5\xfc\x9b\
    \xee\x1f\xdd\xfb\x67\xee\x3d\xa7\x39\xbb\xe3\xec\x78\xef\xec\xba\
    \xfa\xe2\x17\xbe\x9c\x39\x73\xce\xf9\xfe\xbe\xe7\x9c\x99\xf3\x9b\
    \x11\x6b\x2d\x0f\x12\x8a\x07\x0c\x8f\x7b\x80\x74\x7e\xbd\x14\xa3\
    \x5e\x03\x16\xa3\x64\x01\xc8\xc3\x88\x14\x81\x42\x58\xf6\x82\x1c\
    \x02\x75\xd0\xee\x7c\xb5\x87\x49\x62\x52\x5b\x20\x9d\xdf\xac\xc7\
    \xaa\x0d\x88\x2c\x73\x75\xdf\xd3\xcc\x9b\x9e\x63\x7e\x6b\x0e\x87\
    \xf3\xd7\x4b\x5c\xb8\x5e\x24\x30\x4e\x4b\x40\xe4\x24\xd6\x6e\xe1\
    \x89\x23\xbb\x6d\x57\x97\xb9\x6f\x03\xf2\xfa\xb7\x6d\x88\x6c\x43\
    \xd4\xaa\xa6\x9c\xcf\x9a\x65\xb3\x58\x30\xbb\x91\x59\xcd\x59\x94\
    \x30\x1a\x2c\x2a\x5c\xec\xcb\x03\x65\xce\x5e\xbc\xc5\x81\x53\xd7\
    \x18\x2c\x54\x41\x54\x4f\xd8\xf0\x96\xfd\x72\x75\xdf\x3d\x1b\x90\
    \xce\x7d\x2b\x50\x6a\xbf\xaf\x55\x53\xc7\xc2\x56\x5e\x78\xf2\x21\
    \xb2\x19\x1d\x07\xbd\x3e\x54\xa6\x18\x54\xd0\x4a\xa1\xb5\xa2\xb5\
    \xa9\x21\x9a\x3d\xe1\x7d\xc3\x0f\xbf\xdd\xa0\xe7\xf7\x01\xb7\x2a\
    \xfd\x20\xeb\xec\x17\xcf\x7f\x3f\x69\x03\xf2\xc6\x81\xd5\x08\xfb\
    \x66\x37\x67\xf3\x9d\xcb\xe7\xd2\x9c\xf7\x40\x24\x6e\xbf\x7c\xa3\
    \xc0\x3b\x2b\xe7\xb3\xec\xd1\x69\x23\xb7\x4f\xf5\x0d\xb1\xfd\xc7\
    \xf3\xcc\x6c\xc9\x13\x22\x36\xd2\x3f\x5c\x61\xf7\x91\xab\x5c\x1c\
    \x08\x86\xc1\xbc\x62\xb7\xaf\x3c\x74\x57\x03\xf2\xf6\xfe\x47\x30\
    \xfa\x64\x53\xde\x6f\x7e\x73\xf9\x1c\x1a\x73\x5e\x2c\xea\x50\x31\
    \x86\xc5\xb3\xf3\x23\x06\x92\xd8\x7b\xf8\x12\x3f\x9f\xbb\x49\xc6\
    \xd3\x20\xb1\x1a\x37\x4b\x86\xaf\x0e\x5f\x61\xb0\x68\x06\xb1\xde\
    \xd3\xf6\xf3\x67\x7a\xc7\x7d\x0d\xa5\xbb\x3b\xac\xfb\x3b\x32\x19\
    \xbf\x79\xcd\xd2\x99\xa0\x94\x13\x08\x69\x47\xcb\xb2\x09\x1f\xb6\
    \x12\x2f\x2f\x99\x49\x2d\x5e\x5a\xd2\x4a\xdf\xb5\x82\xeb\x13\xd2\
    \x46\x34\x20\xc2\x9a\xf6\x56\x32\x19\xaf\x09\xcf\x6e\x9b\xf8\x1c\
    \x38\xf7\x5c\x27\xa2\x3b\x56\x2e\x9c\x8e\xef\x79\x2e\xa8\xe3\x1d\
    \xa2\x28\xcd\xf1\xbf\x06\xa8\xc5\xaf\x7f\x0f\x21\x9e\x76\x7d\xea\
    \xe8\x6b\xcd\xca\xb6\x69\x20\x7a\x95\xbc\x7f\x6c\xfd\xf8\x06\x94\
    \xb7\xb1\x65\x6a\x86\xa9\xb9\x8c\x9b\x75\xaa\x58\x05\xc5\x9e\xa3\
    \x57\xdc\x83\x16\x0f\x0b\xaa\x96\x9d\xbf\x5c\xc4\x2a\x7f\xb4\x5f\
    \xe9\xb6\x61\xb8\x19\xd8\x90\x26\xd4\xf4\x69\x69\xcc\x80\xa8\x0d\
    \xa9\xcf\x80\xbc\x7b\xb0\x23\x54\xf8\x69\xd1\xdc\x29\xcc\x6b\xc9\
    \x46\xfb\x28\x89\x9e\x32\x52\x77\x65\xa5\x6a\x30\x41\x99\x17\x17\
    \x36\xe3\x6b\xc5\x77\x67\x6e\x80\xd7\x80\x17\xef\xbf\x24\x5f\xd1\
    \x78\xdc\x85\xfe\x80\x33\x97\x4b\x60\xec\x32\xfb\x69\xfb\x89\x3b\
    \x4f\x42\xf1\x56\x29\x25\x34\xe5\x33\xce\x7d\x3c\xbe\xde\x88\x1d\
    \xad\xeb\x2c\xfb\x7b\x8b\x23\xd7\xa2\x72\x60\x00\x37\x0e\x89\xba\
    \xdb\xc8\x34\x71\xdd\x9d\x25\x4a\x55\x30\x0a\x77\x9a\xd6\x18\xd0\
    \x7a\x45\x3e\xa3\x19\x0e\x6c\x62\xc6\x49\x41\xa8\x9a\x2a\x85\x62\
    \xa9\x6e\x76\x0e\xb9\x5c\x16\xed\xe9\xa4\xc9\xd4\x49\xe4\xb3\x9e\
    \x9b\xe0\xe2\x94\x5c\xa0\xda\x6e\x06\xf0\xc7\xd5\x80\x19\x4d\x3e\
    \x9e\x8a\x4c\xc4\x82\x50\x28\x94\xf8\xf7\x93\xa7\x48\xc3\x9c\x4d\
    \x27\xc8\x35\x4e\xa9\xd9\x2e\x62\x54\x0c\x5c\xbd\x55\xa1\x50\x71\
    \xf7\xd5\x82\x7a\x03\x4a\xe7\xdc\xc0\x42\x55\xb8\x30\x50\x21\xeb\
    \x6b\x72\x19\x8d\x56\x63\x4b\x59\xae\x30\x2e\xdc\xc9\x5b\x2d\xd7\
    \x9a\x16\xaa\x36\xd4\x0c\x0c\xc5\xaa\xdb\x7a\x05\x4a\x70\x49\x2c\
    \xcd\x40\xf6\xb6\x7b\x83\x30\x5c\x71\x34\x23\xc7\xac\x16\xd7\x6c\
    \x11\x69\x20\xfb\xc1\x31\x6c\x25\xa0\xf4\xd9\xb3\x24\xe1\x66\x56\
    \x0e\x24\xce\x0b\x66\x24\x38\x21\xad\x13\x77\xba\x11\x09\x21\xc5\
    \x14\x03\xea\x12\xc8\xe3\x77\x2c\x9f\x13\x89\x84\xa8\x46\xf7\xbd\
    \x7c\x78\x3d\x4c\x2d\x4a\xc6\xf5\x89\xf6\x3a\xa9\x21\xc9\x7a\xdc\
    \x5e\x48\x31\xe0\x9d\x05\x42\x03\x35\x22\x50\x2f\xa0\x34\xf5\x50\
    \xee\x7e\xd4\x2f\x75\x5c\xb2\xde\x5b\x7f\x10\x29\x75\x3a\x24\x88\
    \x76\x42\x51\x19\x89\xaa\x9a\x6b\x51\xd4\x61\xbc\xbe\xe9\x9a\x87\
    \xea\x0d\x88\xec\x19\x1b\x94\x1e\x3c\xa6\x08\x75\x10\xe5\x58\x13\
    \xb0\x56\x23\xae\x1f\xac\x33\x60\xb7\x2e\x3a\x81\xe8\xe3\xa3\x02\
    \x2a\x76\x5c\x2f\xe8\xa8\x53\x57\x60\x72\x13\xf0\x4e\xda\xae\x19\
    \x3d\xe9\xb9\x40\xeb\xad\x09\x91\x7b\xda\x82\xd8\x74\xfa\x2a\xb8\
    \x32\x5a\x39\xb3\x65\xdc\x64\x64\x3f\x7e\x6c\x97\x5b\x9e\xd1\x60\
    \x29\xab\x60\x81\xe2\x2d\xa8\x94\xa9\x43\x10\x84\x6d\x43\x60\x6d\
    \x62\xef\xc5\x31\xe9\xb2\x07\xdb\xba\x7b\xe2\x0f\x92\x0f\xff\x69\
    \x43\xd9\xa3\x20\x4d\x75\x4f\x73\x61\x10\xbb\xa5\x8d\x89\x20\x9b\
    \xfe\x84\xfc\x34\x52\xd0\x8f\x55\xed\xb6\xab\xa5\x6f\xc2\xff\x02\
    \xfb\xd1\xdc\x5e\x94\x5e\x8b\x52\xc3\xb5\x5b\xe0\x79\x8a\xbb\x61\
    \x5a\x83\xe0\xd7\x77\x1b\xc6\xb2\xce\x05\x9f\xfc\x47\x69\xf7\x95\
    \xd5\x58\xd9\x8b\x48\xb3\xab\x6b\x25\x34\xfb\x55\xb2\xd5\x61\x1a\
    \x33\xc2\xd4\x90\x2e\xbd\xe7\x7c\x77\xd4\x8e\xe6\xfe\xa1\xb2\x25\
    \xf0\xa7\xd0\x1f\x28\xae\x17\x62\xdd\x41\xc4\xac\xb5\x9b\x67\x1c\
    \xba\xf7\xcf\xf2\xee\x1b\x8f\x20\x76\x07\xd8\x0e\x57\xcf\x7a\xa1\
    \x89\xac\xb8\x59\x3a\x13\x61\x70\xd0\x22\xee\xb8\x0d\x4d\xc0\x7f\
    \x05\xe3\x02\x8f\xa4\xf3\xe8\x17\xe1\x60\x78\xf1\x9e\xed\x6a\xed\
    \xbd\xef\x1f\x13\xe9\x46\x21\xd7\x3a\x41\x6d\x04\xdb\x7e\x3b\xb1\
    \x2a\x19\xa5\xc8\x58\xb6\x33\x63\x52\xc7\x11\xbb\xd5\x6e\x6e\xdd\
    \x75\xdf\x7f\x46\xe9\x66\xae\x76\x20\xb2\x2a\xe4\x0a\x2c\x6d\x40\
    \x2e\x64\x36\xa4\xcb\x23\x67\xc1\x9e\x46\xcc\x9e\x70\xb9\x4f\xdc\
    \xd7\xaf\x99\x88\x28\xc0\x8f\xe8\x25\x4a\x47\x9d\xa0\x8a\x28\x8c\
    \xc1\x09\x99\x88\xd5\x04\x2b\x11\x83\x44\x19\x84\x71\x4d\x9c\x8c\
    \x24\x84\x13\x8c\xea\x99\x44\x70\xc7\x4c\x0a\x1b\x12\xed\xe2\x02\
    \x45\xc2\xe5\x14\x06\x35\x46\x8d\xa3\x0b\xe9\x4c\xfc\x0f\x11\x14\
    \x40\x2a\x88\x72\x5f\x55\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\
    \x60\x82\
    \x00\x00\x05\x9d\
    \x89\
    \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
    \x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\
    \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x1b\xaf\x00\x00\x1b\xaf\
    \x01\x5e\x1a\x91\x1c\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xd7\x0c\
    \x1d\x0d\x3b\x2b\x3c\xba\x61\x5f\x00\x00\x00\x06\x62\x4b\x47\x44\
    \x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x05\x2a\x49\x44\
    \x41\x54\x78\xda\xd5\x97\x5b\x6c\x14\x55\x1c\xc6\xbf\x33\xe7\xcc\
    \xec\x95\x6d\x69\x4b\x85\xda\x5a\x50\x50\x44\xd1\x86\x40\x13\x2c\
    \x10\x25\xf8\x86\x6f\x26\x44\x04\x9f\x0c\x89\xc6\x4b\xc4\x10\x13\
    \x82\xbc\x98\xf8\x60\x22\xbe\x98\x18\x8c\xd1\x44\x13\x44\x1f\x7c\
    \x40\xb9\x04\x05\x15\xd3\x16\x6d\x4b\xa1\xb6\x20\x2d\xa5\x84\x5e\
    \xe8\x6d\xdb\x5a\x76\x77\x76\xae\xc7\xff\x9c\x6c\xb6\x81\x00\xdd\
    \x6c\xd6\x07\xbe\xcd\x97\x73\x66\x67\x76\xff\xbf\xf3\xfd\xe7\xcc\
    \xb6\xb8\x6f\x24\xa5\x64\x81\x0b\xb8\x4e\xb9\x50\x31\x14\x20\xdb\
    \xf1\xd8\x9f\xff\x24\xdb\xbe\x3c\x7e\x65\xcd\xd9\x8b\x93\x00\x24\
    \x59\x0d\xf9\xb9\xcc\x8d\x75\x8b\x62\xd8\xb3\xed\x89\xd6\xe7\xd7\
    \xd6\x6c\x60\x8c\xc9\x92\x00\x6c\x7d\xef\x98\x26\x38\xf3\xde\x7e\
    \xa5\x01\x2c\xf7\x11\x79\xf7\x0c\x70\xe4\x64\x9f\x7f\xe0\xcd\x8d\
    \xa2\x10\x00\x81\x02\x74\xf1\xca\xb0\x2a\x3b\x32\xb1\x0c\x85\xe8\
    \xc8\x6f\x3d\xf8\xe4\xad\x4d\xaa\x78\x49\x00\x3c\x2b\x05\xc6\x80\
    \x4c\xc6\xbc\x47\xef\xd5\xa0\xae\xf3\xec\x34\x94\x4a\x09\x00\x06\
    \xa4\xd3\x19\xcc\x27\x8d\x08\xfc\x92\x03\xb8\x59\x30\x04\x00\x77\
    \x48\xe0\xb6\xa0\xb9\x46\x00\x8e\x59\x5a\x00\xa6\x09\xc6\xb9\x8e\
    \x4c\x01\x09\x08\xae\x81\x1b\x51\x56\x6a\x00\x88\x48\xb9\x4a\x60\
    \x3e\xe9\x82\x43\xa7\x6b\x8b\x01\xc8\x3f\x40\x18\xcb\x2f\x20\x7f\
    \xac\xc7\x2a\x0b\x02\x08\xe9\x02\x46\xbc\x0a\x79\xcd\xf3\xfd\xf9\
    \xd9\x96\x37\x0e\x6d\x74\x3c\x7e\x7c\x64\x7c\x26\xe6\xf9\x00\xe3\
    \x02\x4c\xe3\x60\x8c\xac\xd1\x9c\x73\xac\xad\x4d\x82\xd1\x4b\x7d\
    \x01\x53\xe3\xdc\x43\x48\x4a\xf8\xbe\x54\x2d\x68\x1f\xaa\x80\x63\
    \x3b\x90\xbe\x9b\xb7\xef\x7b\x04\xa7\xa1\x66\x51\x59\xf3\x8b\x9b\
    \xea\x9e\x7b\x7d\xdb\x33\xce\x2d\x09\x68\x6e\x66\xf9\xa2\x44\x22\
    \x56\x5f\x4e\x73\x16\x90\x3a\xca\xf9\xf3\x04\x93\x32\x7d\x82\xd1\
    \x68\xce\x90\x63\xc8\xad\x4c\x59\x01\xd8\xb6\x8d\xc6\xda\x29\x3a\
    \xf6\xc9\xb8\x45\xd1\x68\x04\x95\x35\x2b\x9a\x06\xc6\xa6\xd7\x01\
    \x68\xb9\x05\xa0\xa7\xfb\xc2\x0f\x96\x95\xfd\x70\xf9\xb2\xea\xc5\
    \x0b\xe2\x51\xcc\x15\xd6\x20\x84\x4e\x16\xe0\x64\x4d\x85\xc6\x14\
    \x08\x93\x73\x09\x48\xdf\x87\xe7\x33\xd0\x80\xa9\x7f\x4d\xb8\xae\
    \xa3\xec\x07\x6f\xe4\x40\x1e\x5d\x6a\xe0\xe4\x89\xa3\xa9\x9a\x07\
    \x2a\xfb\x91\x13\x47\x4e\x37\xaf\xff\x65\x69\xf1\x25\x7f\xcf\x4c\
    \x8e\xef\x48\x94\x95\x33\xd7\xf3\xd5\x8a\x28\x7e\x32\x27\xcf\xb5\
    \x04\xb9\x51\x42\x53\x06\xa8\xb0\x64\x04\x01\xf5\x19\x2f\xb0\xe7\
    \x11\x80\xaf\x12\xa1\xc0\xb0\x66\xd5\x52\x9c\x39\xd3\x82\xcb\xbd\
    \xbd\xbb\xba\x8e\x1d\x68\xbe\x1d\x40\xc9\x4e\xf6\xf5\xcb\x70\x75\
    \xc2\xb1\xad\xf5\x91\xf8\x42\x55\x54\x53\xfd\xd7\x73\x23\x1d\xab\
    \xf9\x1c\x94\x02\x02\x15\xcf\x41\x50\x71\x15\xfd\x92\xaa\x72\x3c\
    \x52\x57\x8d\xaa\xf2\x05\xd0\xe8\x8d\xe3\x27\x7e\xc6\x40\x7f\xdf\
    \xa1\xcc\xc0\xa9\xfd\xf3\x6d\xc3\xbd\x53\xe3\x63\x9b\xcb\x16\x56\
    \x37\x68\x62\x31\xb8\xd4\xc8\x1c\x32\x60\x65\x7a\xe0\x7c\x1a\xba\
    \xce\x11\x8f\x18\x58\x10\x35\x08\x84\x0a\x7b\xbe\x8a\xdd\xb6\x6c\
    \x5c\xbd\x3e\x8a\x96\x8e\x3e\x98\x66\x06\x6e\x26\x89\xe4\x8d\xa1\
    \xab\x80\x7c\xad\xa0\x5f\xc3\x70\x6d\xd3\xe3\x46\x38\xd4\xfe\xd8\
    \xd3\x1b\xa2\xd1\x78\x19\xa2\xd1\x28\xc2\x91\x08\x22\xe1\x08\x42\
    \xa1\x10\x8c\x90\x01\x5d\x08\x75\x17\xa6\x4d\x07\x37\xd3\x59\x8a\
    \x5a\xf5\x1c\x8e\x6d\x13\x40\x16\x56\xd6\x44\x96\xec\x64\x53\x18\
    \xbe\xdc\xea\x10\xc8\x26\x6b\xa4\xed\x2c\x6e\x13\xc7\x1d\xe4\xce\
    \x0e\x4e\x22\xba\x24\xe9\x3a\x99\xad\x89\xaa\x7a\xf8\xd0\x21\x99\
    \x4e\xa3\x80\x47\x76\x7c\x0d\x96\xcb\x60\xbb\x80\x0f\x0d\x82\xeb\
    \xe0\x82\xcc\x85\xba\x69\x73\xdb\x54\xed\x96\xe4\xe0\x05\xcc\x24\
    \x27\xf6\x5b\x37\xda\xbf\x05\xa9\x10\x00\x25\x91\xa8\xeb\xc8\x66\
    \x52\x0d\xe5\x15\x65\x2b\xcb\x2a\x1f\xa4\x04\xa2\xca\xa1\x70\x18\
    \x61\x4a\x20\x64\x18\x30\xc8\xba\xae\x43\xe8\x9c\x20\xb8\xda\xaa\
    \x2c\x07\x10\x28\x3d\x7d\x0d\x03\x97\xce\xff\x2a\x25\x76\x79\xa9\
    \x11\x89\x3b\x48\xc3\x5d\x64\x0d\xb5\x40\x4a\xbc\xda\xd7\xdd\x3e\
    \xac\xb1\x2c\x45\x1f\x14\x0e\x51\x1b\x02\x87\xd5\x9e\xce\x3b\x68\
    \x4f\x84\xce\xd3\x39\x32\x41\x46\x20\x84\x83\xde\xf3\xcd\x49\xda\
    \x15\x3b\x69\xf5\x3e\xee\x22\x8e\x7b\xc8\x9b\x1d\x34\x59\xac\xe6\
    \xc2\xec\xd4\x8d\x1d\x2b\x56\xaf\x63\x21\x43\x15\x51\x20\xa1\xc0\
    \x86\x4e\x09\x08\x2a\x16\xac\x9c\xcd\xdd\x52\xcc\x47\xdb\xa9\x43\
    \x98\x1e\x1f\xdb\x6e\x8f\x75\xb6\x81\x54\x0c\x40\x0e\x62\x68\xc0\
    \x96\xb1\xb8\x26\xdc\xa6\xfa\xe5\x4f\x51\x51\x95\x02\x81\x18\x0a\
    \x82\x5a\xa0\xa2\x57\xb1\x4b\x89\xe0\x75\xa9\xf3\x04\x2e\x77\xb4\
    \x7e\x66\x4f\xf4\x1c\xc0\x3c\xd2\x0a\xfb\x8b\xd8\xdd\xd7\xdd\x7a\
    \xfa\xdc\xd8\xe8\x25\xb5\x62\x43\xd7\x55\x0a\x51\x8a\x9d\xac\x60\
    \x8c\x5c\x1a\xd3\x33\x03\x38\x77\xfa\x48\x8f\x94\xde\xbb\x20\x95\
    \x04\xc0\x1e\xeb\xb2\x3d\xc7\x7e\xf9\xd4\xf7\x07\x33\x96\x97\x02\
    \xe7\x3c\x28\x96\xef\xb9\x2e\x74\xf5\x9e\x0b\x0b\xbf\x1c\xfe\x34\
    \xeb\x64\xcd\x97\x9c\x64\xaf\x89\x02\xc4\x51\xa0\x7c\x33\x39\xe9\
    \xfa\xfa\x64\x72\x6a\xe8\x85\xd5\x8d\x9b\xa1\x73\x5d\xdd\xf9\x81\
    \x5c\xd7\x85\xed\xbb\x38\xfa\xdd\xc7\xb8\xd6\xd5\xf1\x8e\x77\x73\
    \xf8\x27\x90\x4a\x09\xa0\xc4\xe3\x8b\x3b\xa6\x06\xfb\x1a\xf4\xaa\
    \xb2\x95\x0f\xd5\xad\x52\xcf\x7d\xd7\xf5\xe0\x10\x40\x07\xf5\xfd\
    \x8f\xc3\x07\x7f\xd4\x8c\xc4\x6e\x3f\x3b\x5d\xc4\x3f\x26\x85\x43\
    \x54\xea\x86\xe8\xda\xf9\xc1\x17\x35\xf5\x15\x0f\x23\xd0\x68\x7a\
    \x14\x5f\xed\xdd\x3e\x62\xce\xa6\x1b\xa8\xf8\x04\xfe\x6f\x69\x91\
    \x8a\x2d\xd5\x4f\x36\x7a\xbb\xbf\xee\x94\x7b\xbe\xe9\x92\xb5\x8d\
    \xcf\x7a\x4c\x8f\x6d\x41\x11\xe2\x28\x42\xd2\x35\xaf\x9a\xb3\x99\
    \xb8\x95\x10\x4d\xbd\xdd\xcd\xe8\xff\xfd\xe4\x47\xd2\x4e\x7d\x8e\
    \x22\x24\x50\xa4\x98\x88\xec\x33\x38\xd6\x8f\xf5\x74\xba\x4c\x44\
    \xdf\x87\x35\x8b\xfb\x52\xff\x01\xf1\xb5\x4c\x52\x7d\xd3\xb5\xd7\
    \x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
    \x00\x00\x06\x3f\
    \x89\
    \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
    \x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\
    \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
    \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x03\x76\x00\x00\x03\x76\
    \x01\x7d\xd5\x82\xcc\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
    \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
    \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x05\xbc\x49\x44\
    \x41\x54\x78\xda\xc5\x57\xcd\x8f\x14\x45\x14\xff\x55\x75\xf7\x7c\
    \x2d\xbb\x0b\xeb\x80\x2b\x2c\x20\x71\x23\x7e\x20\xb8\x06\x02\x09\
    \x07\x49\x38\x70\x20\x1e\x4c\x4c\xbc\x90\x18\x0f\x86\x9b\x37\x13\
    \x4f\x1a\xe3\x81\x70\x37\xfe\x03\x7a\x30\x78\xd0\x4d\xe0\xc2\x45\
    \x49\x44\xc4\x20\xa8\x17\xc3\x02\xd9\x04\x23\x1f\xbb\xec\xee\xb0\
    \x33\x4e\xcf\x74\x77\xbd\xb2\x5e\x75\xa5\x52\xd9\x95\x9d\xf1\x64\
    \xed\xbe\xa9\xcf\xae\xf7\xab\xdf\xfb\xd5\x9b\x1e\xa1\xb5\xc6\xff\
    \x59\xe2\xb0\x23\x4c\x99\x9e\x9e\xfe\xed\xd0\xa1\x43\xfb\x0c\x30\
    \x62\x8b\xe2\x98\x9a\x7b\x8f\x09\xc8\x84\x14\x29\x00\x52\x6b\x01\
    \x4d\x24\x00\x98\x05\x10\xa4\x89\x5b\xae\x40\x98\x39\xd2\x8a\x50\
    \x76\x4d\x5f\x01\x7a\xb1\x95\xf6\x97\x97\x16\x3e\xfb\xe5\xdc\x27\
    \x67\xd7\x02\x08\xcb\x9e\x99\x99\x99\x57\x4e\x9f\x3e\xcd\xed\xc8\
    \xe0\x89\xa4\x94\x88\xa2\x08\x5c\x9b\xbe\xad\xb9\xf0\x18\xf7\x01\
    \xf0\x18\x5b\xd0\x5f\xbf\xf6\xd2\xb5\x39\x9c\xb9\x30\x76\x78\x23\
    \x06\xb8\x6c\x9e\x9d\x9d\xc5\xfc\xfc\xbc\x77\xc0\x9b\x3c\x7f\xfc\
    \x3d\x24\xa3\x93\xa8\xd5\x6a\x58\x5d\xed\x82\x00\x68\xa5\x41\xe0\
    \x03\x02\x05\x09\x83\xd6\x52\x88\xbc\x50\xd0\xc4\x33\xc2\x58\x19\
    \x5e\x52\x05\xf2\xbc\xc0\xb6\x4a\x77\x3f\xd6\x94\x50\x03\xec\x70\
    \x06\xc0\x75\x76\xc4\x25\xcf\x73\x0b\xe2\xf0\xbb\x9f\xe3\xe0\xd1\
    \xe3\x4c\x09\x16\xdb\x05\x1e\xae\xb2\x63\xfb\xef\x8b\x34\x73\xbb\
    \x9b\x11\xfe\x5c\x52\x20\x5d\x82\x01\x57\x80\x5b\x6b\x40\x4a\x05\
    \x95\xd7\x26\xce\x7f\xb8\x6b\xc5\x3f\xb7\x06\x90\xb2\xb4\xc4\xb1\
    \xa7\x91\x2d\x2f\x34\xee\x2e\x13\xba\x19\xe1\xb9\x6d\x15\x48\x61\
    \x1d\xfa\x79\x6e\xc3\xd8\xc3\xc7\x1a\x93\xe3\x11\x84\xe4\xb1\xd2\
    \x44\x50\x6b\x1d\x23\xa9\x59\x78\x78\x12\x00\xe2\x8f\x24\x49\x7c\
    \x08\xb8\xe6\x0d\xd2\x4c\x63\xb5\x07\xb4\x7b\x84\x9d\x4f\xc5\x6e\
    \x63\x6b\xde\x61\xa6\x60\xc7\x93\x48\xc0\xcf\x4b\xdb\xf6\xeb\x54\
    \xa3\x9a\x6d\xa4\x01\x0f\x80\x8b\x17\x58\x54\xb2\xd1\xea\x6a\x54\
    \x62\x8d\x67\x9b\x31\xee\xb5\x72\x4c\x3f\x5d\xc5\x4b\x53\x35\x6c\
    \x1d\x8b\x11\x4b\x81\x07\x2d\x0e\x4f\x0e\xd2\x29\x1e\x75\xb0\x26\
    \x4c\x0c\x04\x68\xdd\xbf\x99\x01\xcf\x3c\x11\x80\xa5\xa7\x52\xa9\
    \x80\x88\x3c\x03\x16\x44\x19\x53\xb3\xb1\xc6\x8e\x2d\x02\x1f\xbd\
    \x39\x89\x5d\xcd\x0a\xe6\x17\x33\x7c\xff\x47\x07\x2b\x1d\x85\xfd\
    \xbb\xeb\x78\xfb\xc8\x16\xf4\xf2\x71\x7c\xf9\x43\x0b\x73\x0f\xfa\
    \xa1\xbe\x78\x73\xfd\xdd\xc7\xc7\x8a\x8d\x18\x50\xbc\x90\x19\x28\
    \x8a\x82\x2f\xb2\xd3\x82\xd3\x03\x80\x46\x55\xe2\xad\x23\x4d\x4c\
    \x4d\x24\x38\x77\xb5\x85\x8b\xbf\xb7\xfd\x51\x6f\x3d\xcc\x70\x79\
    \xee\x6f\x7c\x70\x72\x1b\xde\x3f\xd1\xc4\xd9\xf3\x8b\xf8\x6b\x39\
    \xf7\xa7\x37\xfb\x59\x8d\x6d\xa4\x01\x1d\x9c\x9a\x1f\x70\x1a\x90\
    \x3e\x86\x07\x76\xd5\x8d\xd0\x12\x28\x02\xb2\xbc\x08\xb5\x60\xdb\
    \x0b\x8f\x15\xae\xdc\xee\x22\x89\x05\xde\x78\x6d\x0c\x22\xd0\x81\
    \xf9\x28\x86\x01\xc0\xb7\x80\x1d\x7b\x00\x60\xd5\x3b\xa1\x6d\x1d\
    \x4f\x7c\xac\x5e\xdc\xd1\xc0\xc4\x88\x17\xa1\x07\x79\x7f\x85\xd9\
    \x03\x76\x4e\x24\x81\x08\xd9\x30\x90\x01\x0a\x32\x5f\x68\x7e\x93\
    \x1b\xf3\x29\xc8\xe5\x8e\x9b\xf7\x33\x8c\x54\xa5\xb1\xd0\x89\xb0\
    \xda\xe0\x72\x67\x21\x87\x40\x70\x1d\x81\x7c\x50\x26\xd4\xec\xcc\
    \x31\xe0\x45\x28\x00\x2f\xc2\x85\x36\xe1\xd3\x6f\x17\x51\x4d\x24\
    \xee\x99\xf8\x8e\xd5\x81\x24\x92\xa0\x58\x23\x27\x60\xbc\x1e\xe1\
    \xe0\x9e\xba\x65\xe8\xa7\xdb\x29\x2a\xb1\x44\x41\x65\x5e\x52\x40\
    \x31\x34\x80\xf0\xf4\x5a\x53\x29\x42\x97\xd6\x96\x3a\xa6\x0f\xb2\
    \x74\x77\xfa\x02\x13\x0d\x80\xa8\x9c\x3f\x75\x74\xdc\x32\xf2\xf5\
    \xcf\x6d\xcb\x40\x25\x12\x20\x4f\xb7\x18\x8e\x81\x30\x04\x96\x01\
    \x22\x66\xc0\xa7\x54\x20\xbc\xe3\x06\x44\x06\x54\x63\xe0\xc8\x74\
    \x1d\x7b\xb7\x57\xf1\xcd\xb5\xb6\x11\x62\xcf\x3e\x43\x5a\x20\x96\
    \x5c\x6b\xe8\x61\x00\xb0\xc3\xb5\x00\x2c\x03\x2c\xa6\x20\xa1\x04\
    \xe9\x05\x4a\x03\x23\x35\x81\x13\x07\x36\xe1\xca\x5c\x6a\xae\x62\
    \xe9\xdc\x16\xc7\x9c\x64\xce\xa4\xe8\x0f\x66\x20\x14\x9d\x6b\x43\
    \x68\xde\xd0\x3b\x74\xff\xde\x01\xb7\x8f\xbf\x3c\x8a\x7b\x2b\x39\
    \x66\xaf\x77\x8c\x3e\x80\xbc\xb0\x8b\x1c\x60\xed\x6a\x91\x0f\x75\
    \x0d\x01\x78\x01\xba\xbe\x53\xb8\xf4\x57\x4e\x94\xe6\xdb\xfb\xa6\
    \x2a\xf8\xea\x6a\xd7\x8a\x53\x91\x30\xe2\xb3\xeb\x1d\x70\x59\x5e\
    \x63\x88\x6c\x10\x03\x9e\xfe\x10\x84\x77\x18\x1c\x5d\x04\xa7\x67\
    \x67\xfd\x42\xa3\x9d\x6a\x44\xd2\x7e\x19\x59\xe5\x1b\x2c\x50\x3a\
    \xd0\x8c\x18\x32\x04\xec\xcc\x59\x98\x03\xbc\x43\x2e\xa1\x20\xb7\
    \x8c\x44\xb8\xbb\xa4\xdc\x57\x2e\x20\xa4\x46\x1c\x01\x44\x12\x31\
    \x83\x70\x80\xcd\xf4\x70\x0c\x84\xce\xd9\x84\xa3\xde\xcb\x30\xf8\
    \xe0\x2a\x57\xc0\x8f\xb7\xfa\x0e\x24\x4a\xe5\x47\xda\xce\x6a\xde\
    \x13\xb0\xb5\x94\x51\x6f\x68\x06\xc2\x70\xc8\xb8\xc2\x69\xd9\x87\
    \x85\xd8\x8f\xd6\x3e\x24\x8f\x53\x32\xc6\x73\xce\xcc\x9f\x22\x06\
    \x21\x40\x04\x2f\x5e\x21\x68\x78\x00\x21\x03\x32\x32\x00\x08\xd0\
    \x56\xcd\x1a\xc2\xc5\x95\x40\xd8\x54\x91\x78\xfd\x85\x32\xf3\x5d\
    \xbd\xd3\x43\x2b\xd5\x90\x28\x55\x9f\x6b\x3e\xb5\x46\xd9\x95\xfc\
    \xec\xc0\x4c\x88\x38\xf6\x43\x01\x03\x55\x28\x9b\x8c\x80\xf2\xdc\
    \x64\xc3\xc2\xe5\xe4\xab\x0d\x93\x7a\xab\xb6\xdd\xdc\x24\xf1\xc5\
    \xe5\x36\xb4\x70\xe4\xf3\x7a\x72\xda\x81\x3b\xc1\x20\x06\x82\xd7\
    \x69\x9f\x88\x10\x25\x20\x4d\xc6\x00\xe1\x63\x4f\x96\x8d\x7a\xc5\
    \x1d\xd0\xd8\x68\x4d\x80\x4a\xaa\x7c\x18\xc9\x31\xc6\x5d\x8e\xc1\
    \x7f\x12\xa1\xbf\x92\x32\xb1\x0c\x78\xdd\x07\x0c\x5c\xb8\xd1\xc6\
    \xe6\x86\x44\x35\x16\x38\xff\x6b\x07\x4a\x31\x4a\x1d\x5e\x53\xdf\
    \x8e\xa4\x1c\x00\xc0\xad\xcd\xb2\xcc\xbe\x92\x13\x91\x7d\x33\x02\
    \x34\x94\x22\x2f\xc2\xb2\xa1\x18\x8e\xb9\x7e\x19\xce\xcc\x2e\xf2\
    \x78\xa9\xf4\x20\xfd\xc2\xb5\x7d\x13\x83\x43\xb0\xdc\xeb\xf5\xa8\
    \xdd\x6e\x4b\x53\x5b\x10\x69\x9a\x22\x2f\x08\x91\x22\xde\x6b\x7d\
    \x3a\x76\x83\xee\x13\xb4\xde\xb1\x0f\x5b\x22\xb5\xdc\x10\x80\xd6\
    \xba\x3f\x39\x39\xf9\xa0\xd3\xe9\x6c\x67\x00\x4a\x29\x0b\x80\x54\
    \x8e\x42\x29\xe7\x58\xff\x9b\x63\xae\xd7\x83\x0a\xd6\x70\x29\xe4\
    \xc6\x0c\xf0\x03\xf1\xe8\xe8\xe8\x45\x29\xe5\x3b\x06\x80\x7f\x89\
    \x4c\xdb\x8f\xd0\x27\x19\x0b\x11\xf1\x6e\x4e\xac\x0c\x18\xc1\x77\
    \x85\xb4\xe3\x01\x30\x46\xea\xc7\xa5\x20\xa5\x62\x5c\x12\x62\x6a\
    \x0c\x40\xd7\xec\x5d\xf8\x9f\x66\x66\x13\xe9\xc0\xd4\x8c\x35\x8c\
    \xd5\xd9\xdc\x58\xe2\x6b\x6f\xe1\xb8\xff\x45\x55\x38\xcb\xbd\xf9\
    \xbe\x9f\xeb\x3a\x4b\x8d\xf5\x19\xc4\x3f\xde\x21\x68\x33\xad\x41\
    \xbd\x0c\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
    \x00\x00\x07\xba\
    \x89\
    \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
    \x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\
    \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
    \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x03\x76\x00\x00\x03\x76\
    \x01\x7d\xd5\x82\xcc\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
    \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
    \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x07\x37\x49\x44\
    \x41\x54\x78\xda\xe5\x96\x5b\x6c\x5c\x47\x1d\xc6\x7f\x33\xe7\x9c\
    \xbd\x78\xd7\x9b\xf8\xb2\x76\xba\x8e\x9d\xd8\x1b\x87\x80\x9a\x34\
    \x8e\x03\xa2\x4a\x9b\x26\x41\x11\x14\x2a\x05\x29\x28\x3c\x81\xe0\
    \x09\xf1\x10\x45\x15\xa2\x02\x44\xb9\x88\x07\xc4\x03\x0f\x91\x2a\
    \x95\x20\x21\x90\x68\x04\xaa\x84\x78\xa0\xb4\x82\x2a\x29\x09\x20\
    \xa4\x12\xaa\x24\x2a\x95\x13\xc7\x8e\x1d\x37\xf1\xae\xed\xd8\x5e\
    \xaf\xf7\x76\x2e\x33\x9c\x9c\x73\x76\xd7\x89\x40\x8a\x68\x24\x1e\
    \xf8\xec\x4f\x33\x3a\x33\xf3\xff\x7f\xff\x6f\x66\xce\x59\xa1\xb5\
    \xe6\x51\x42\xbc\x24\x86\x48\xf2\x5d\x1a\x1c\xc4\x23\x0d\xa4\x30\
    \x59\xc6\xe5\x1c\x9b\xf8\x91\xfe\xa2\xbe\xce\x06\x3c\x32\x01\xe2\
    \x8c\xb0\x10\xfc\x0c\x9b\x13\x07\x47\x0f\xc6\x8f\x0e\x1e\x25\xdf\
    \x99\x07\x01\x53\xa5\x29\xde\x9c\x7b\x93\x8b\xd3\x17\x35\x82\x5f\
    \xf3\x16\x5f\xd0\xaf\x6a\xef\x91\x09\x10\x27\x84\xc1\x21\xce\xc5\
    \x3b\xe3\xcf\x9c\xda\x7f\x8a\x5d\x99\x5d\x78\xca\xa3\x56\xab\x81\
    \x80\x64\x22\x89\xa1\x0d\x26\x56\x27\x38\xfd\xee\x69\x1a\xab\x8d\
    \x3f\x63\xf0\x09\xfd\x15\xed\xc8\x0f\x9c\xdc\x07\x07\x79\x45\x64\
    \xc4\x33\xc7\x77\x1d\x27\x91\x48\x30\xe7\xcc\x30\x51\xb8\xcc\xc9\
    \x9d\x27\x39\x39\x7a\x92\x1b\x0b\x97\x59\x60\x86\x8e\x54\x82\xe3\
    \x3b\x8f\x23\xd2\xe2\x69\x6c\xce\x0a\x1f\x26\x1f\x14\x2f\xf1\x11\
    \x04\x9f\xcf\x6d\xce\x61\xa6\x4d\xe6\xed\x19\x3a\x4d\x58\x29\x2f\
    \xd0\xc4\x6a\x79\x8a\x78\x4f\x99\x75\x20\xd9\x61\x92\x7b\x2c\xc7\
    \xed\xd2\xed\xcf\xf2\x22\x79\xd3\x17\x11\x03\xf6\x01\x89\xff\xc6\
    \x00\xbe\xcd\x37\x18\x46\xa4\xba\x53\xbc\xdf\x98\xa1\x37\x06\x31\
    \x03\x2a\xd5\xbb\x34\x51\xab\xcd\xa1\xa8\xd2\xd0\x50\x06\xd2\x56\
    \x0a\x36\x63\xe1\xf1\x43\x13\xd8\x37\x35\x7d\xf3\xb7\x3d\xbd\xd9\
    \x2d\x20\x90\x22\xf0\x95\xb0\x11\x3e\x01\xc2\x36\xe8\x4a\x41\xf8\
    \x07\x8e\xeb\x90\x3f\x3b\x42\xb1\xaf\xc8\xe4\xe2\x24\x4b\xf3\xb3\
    \xe4\x12\x1d\xf4\x58\x82\xda\xbc\x03\xcf\x11\x60\x66\xe2\x16\xa5\
    \xf2\x3c\x2b\x8e\x66\xbe\x51\xa5\xb8\xc5\x06\x0d\x38\x1c\x34\x81\
    \x44\x77\x4f\x76\x8b\x65\x99\x18\x41\x16\x11\x95\xc6\x03\x7d\xa2\
    \xb4\xa0\xa3\x67\xca\x53\x94\x65\x19\x6c\xd0\x37\x34\x77\x5f\xa8\
    \xf3\xef\x70\xe1\xf9\x35\x36\x22\xf3\x63\x01\x23\xc0\x12\x49\x33\
    \x2a\xca\xa7\xe0\x0f\x57\x97\x79\x18\x7c\x66\x5f\x16\x0d\x54\xab\
    \x55\x4c\x65\x82\x02\x04\x0f\x0d\x43\x00\x16\x90\x26\x6e\xd2\xae\
    \x11\x43\xde\x1f\x45\x69\x85\x52\x1a\x43\x48\xc4\x86\x31\x8d\xc0\
    \x53\x9a\xc9\x1b\x37\x88\x27\xe3\x90\x04\x46\xa1\xe7\x27\x82\x6c\
    \x0c\xb2\x71\x70\x0b\xf0\xb7\xaf\x69\x00\x3e\x79\x5a\xd0\x99\x83\
    \xa2\x0d\x8b\x0d\x30\x76\x01\x35\x40\x52\x37\x81\x20\xb8\x7e\x40\
    \x80\x5b\x5e\xc4\x98\xfd\x13\x9e\x6b\xb3\xb0\xf9\xa3\x6c\x19\xda\
    \x81\x94\x12\x80\xba\xa3\xf0\x3c\x4d\x57\x4f\x1f\x23\x37\x77\xb0\
    \xe8\x2c\x82\x05\x62\x07\xa4\x52\xd0\x97\x04\x27\x45\x0b\xfd\x79\
    \xe8\xde\x09\xba\x0a\xd5\x1a\xdc\x5c\x8d\x1c\x98\xe5\x86\x6c\xd6\
    \x2f\x42\x01\x01\x85\xd0\x58\xb3\x7f\x44\x57\x8a\xf4\x6f\xee\x60\
    \x3c\x73\x87\x4a\xb5\x81\x14\x91\x48\x0d\x4a\x6b\xfa\xfa\x73\x7c\
    \x35\x7b\x0a\xd3\x08\xb7\xa1\xb4\x0e\xcb\x3e\x8b\x3e\xd7\x14\x2d\
    \x2c\x03\x73\xa5\xf0\xf9\xed\x12\xd8\x4e\x94\xf5\x35\x5e\x93\x4d\
    \x07\x40\x60\x88\x90\x42\xc3\xe6\xae\x0c\x47\x3e\xfd\x1c\xfb\x9e\
    \x3a\x82\x27\xe2\xdc\x29\x2e\x61\x44\x73\x74\xb4\x0d\xc2\x30\x78\
    \x72\xf4\x29\x76\xaf\xef\x85\x38\xb8\x40\x61\x35\x4c\xb2\xe8\xd1\
    \xc2\x82\x0b\x33\xab\x30\xe5\x73\x61\x1d\x90\x30\x5e\x1e\x87\xcb\
    \x5c\x0d\x05\x44\xd7\x4c\x4a\x11\xd0\x90\x90\xd9\x94\x41\x98\x09\
    \x84\x34\xe9\xec\xdf\x4a\xcc\x92\xcd\xf1\xa0\x7a\xcf\x53\x14\x0b\
    \x05\x84\x21\x79\x21\xf9\x22\xdb\xd7\x86\x21\x0d\xf5\x24\xdc\x5a\
    \x81\x39\x1b\x1e\x3b\x2b\x18\xf0\x39\x5b\x87\x77\x97\xa0\x60\x83\
    \xde\x04\x83\x95\x61\xbe\xd3\xf5\x7d\x80\x35\x09\x41\xe2\x8d\x5b\
    \x10\x58\x5a\x6c\xa4\xd1\x6e\x1d\xed\xd9\x5c\x7a\x6f\x8e\xd1\xa1\
    \x01\x4c\x53\x06\xe3\xe9\x84\xe1\x53\xe2\xda\x55\x56\x97\x97\x18\
    \xcc\x0d\xf1\xfc\xe2\x37\x79\x7c\xf1\x71\x48\x81\xea\x87\xb2\x09\
    \x85\x38\xdc\x49\xc2\x42\x0c\xec\x2c\x98\x19\x93\x0f\x17\xf6\xf0\
    \xf5\xf5\x1f\xb0\x77\xcf\x3e\x00\xd7\xa4\xf9\xc2\x91\x61\x70\x22\
    \x21\x5e\xd7\x4e\x6e\x4d\xfd\x05\xab\x63\x13\x85\x92\x62\x20\xd6\
    \x1e\x47\x83\x29\x05\xfd\xbd\x5d\xfe\x56\x75\x71\x67\xb9\xc1\xd3\
    \x99\x1e\xf2\xd3\x43\x5c\xb8\x7c\x9e\xf3\xd9\xf3\x4c\xe6\xae\xd3\
    \x50\x36\x96\x34\x31\x1d\x8b\x81\xf7\xb7\x71\xa8\xf0\x2c\xfb\x06\
    \x3f\xc6\x87\xc6\x77\x93\x48\x26\x01\x74\xf4\x1e\x68\x3a\x00\x5a\
    \xeb\xc0\x62\x71\xb7\xc0\xc0\xee\x8f\x33\x71\xe5\x6d\xb6\xc5\x12\
    \x78\xae\x87\x69\xdc\x73\x48\x06\xd7\xd3\x53\x8a\x89\x6b\xd7\x70\
    \x6c\x07\x4f\x58\x34\x5c\xf0\x3c\xc1\x78\x6e\x9c\x7c\x69\x07\x72\
    \x5a\x50\xa9\x54\x10\xd2\xc2\xd6\x16\xbd\xd9\x7e\x86\x8f\x8c\x92\
    \x4c\x77\x92\xb0\x2c\x24\x1a\x80\x50\x80\x0c\xef\xb9\xff\x4f\x6d\
    \xe2\x9f\x24\xff\x71\x91\x9c\x5b\x66\xf5\xed\x04\x9b\xb4\xe0\xb0\
    \x84\x1b\xb3\x33\x5c\xef\x19\x66\xfb\xe1\x43\x68\xa5\x7d\x2a\x9e\
    \x78\x62\x2f\xb1\x58\x8c\xf5\x9a\xcd\xfc\xd2\x3a\xd2\x30\xe9\x4a\
    \xc7\xd0\x4e\x05\xdb\x76\xb0\x2c\x0b\xdb\x71\x71\x45\x8c\x98\x29\
    \x48\xf8\x73\x4d\x23\x2a\xd6\x90\x6d\x01\x22\x3a\x03\xac\xde\xa5\
    \xef\xf5\x57\x30\x57\x57\xa8\x3b\x36\x46\x3c\x4e\xdc\x67\xd5\xe7\
    \xd6\x54\x8a\xed\xbd\x1d\xfc\xee\xe2\x5f\xd9\x3f\xf2\x6c\xe0\xd2\
    \x95\x2b\x97\x83\x2a\x1d\x1d\xa3\xd6\xb0\x31\x4d\x8b\x78\x22\x41\
    \x2a\x26\x30\x50\xd8\xae\x13\x08\xcd\x64\x32\x81\x83\x8e\xe7\x06\
    \x9f\xeb\xe1\xe1\x91\x60\x7d\x5b\x00\x21\xec\x73\x6f\x20\x57\x57\
    \x02\xf5\xf6\xd4\x74\x20\x40\xfa\x34\x12\xf7\xda\x18\x5d\xb9\x01\
    \x06\xca\x93\x28\xfd\xa9\xe0\x0d\x39\x3e\xbe\x9f\x4a\xdd\x63\xb9\
    \xe2\xb2\x56\x2a\x91\x4a\x67\x00\x45\xcd\x17\xd5\xd9\x11\xa3\x77\
    \x53\x12\x00\x8d\x0e\xb6\xd6\x75\x1d\xea\xb5\x7a\xe8\xa0\x56\x34\
    \x73\x1f\xf2\x3c\xef\xad\x7b\x01\x43\x84\xad\xd6\xf7\x18\x2c\x05\
    \xdd\x0c\x12\x0e\x7b\xca\x43\x29\x85\xe3\x2a\x16\xd6\x1c\x3c\x15\
    \xce\x25\x9a\xaf\x82\x04\x20\x25\x24\x2d\x81\x65\x80\x25\xc3\xab\
    \x5e\x77\x3c\x6c\x47\x63\x5a\x16\x3b\xb7\x65\x0f\x9b\x00\x2a\x38\
    \x78\x0a\xc2\xa4\x21\x89\xfa\x11\xdb\x22\x74\x4b\x5c\xa9\xe6\xe1\
    \x79\xcd\xf9\xe1\xe1\xd5\xd1\x0d\xf1\xb4\xc2\x75\xa1\xe4\xa8\xd6\
    \xba\xa0\x46\x15\xf6\xd3\x29\xa3\xbd\x05\xbe\x25\x21\x69\x05\x0f\
    \xc8\xfd\x42\x1e\x74\x25\xd8\xeb\x6a\x2d\x10\xdf\xfa\x20\x66\x12\
    \x32\x78\xae\xb5\x08\xbe\x19\x2b\x55\x8d\xed\x36\xdd\x53\xa8\xa8\
    \x90\x84\xa9\xdb\x02\x66\x66\x67\xb8\xf4\xf7\x4b\xad\xc0\x40\xab\
    \xdf\x6e\x81\x76\xbf\x25\xce\x53\x9a\xaa\xad\x30\x04\xc4\xcd\xd0\
    \xe6\x68\x18\xd0\x81\xdd\xb5\x7b\x2e\x34\x63\x45\x42\x0f\x3e\xb9\
    \xbf\x2d\x60\x70\x70\x88\x81\xdc\x00\x7a\x83\xcd\x00\x52\x48\xa0\
    \xed\x8c\xeb\x86\x7b\xaf\xa1\xed\x90\x0e\xf7\x1c\x11\x3e\x13\x42\
    \x60\x1a\x46\x4b\xa8\xed\x29\xe6\x96\x6a\x38\xae\x87\x8e\xdc\xee\
    \x88\x49\x72\x3d\xc9\x30\x47\x24\xb7\x79\x0e\x5a\x07\xe8\x8d\xd7\
    \x7f\xff\xad\x74\xba\x63\x2c\x9d\x4e\x8d\x75\xa6\xd3\x63\x99\xce\
    \xf4\xd8\x4f\xcf\xbc\xfc\x72\x18\x44\xf9\x0c\x13\xab\x0d\x07\x4f\
    \x20\xf8\xd5\xd9\x5f\x9e\xce\xf6\x76\x8f\x65\xb3\xdd\x63\x7d\x3e\
    \xb7\x6e\xe9\x1d\xbb\xf6\xce\x85\xef\x99\x52\xa2\x3d\x8d\x56\x60\
    \x49\xd5\xba\x05\xd1\x21\x8c\xaa\x68\x55\x2f\x38\x76\xec\xd8\x35\
    \xbf\x7f\x99\x08\xc2\x47\x3e\x9f\xbf\x45\x33\x69\x8b\xb4\x4f\x3f\
    \x92\x03\x07\x0e\xbc\x07\x5c\xd1\x3e\x88\x50\x2e\x37\xf2\x57\xa7\
    \x8b\x2d\xb1\x31\xd3\xa0\x39\x6a\x02\xf5\x62\xa1\x78\xa7\xaf\x2f\
    \x9b\xd3\x88\x56\x50\xdb\xf1\x7e\x53\xa9\xae\xfc\xa2\xbb\xab\xeb\
    \xcb\xc2\xc7\xd4\xd4\xf4\xab\xdd\xbd\xbd\x9f\x53\x9e\x42\x20\xa1\
    \x7d\x18\xef\xdb\xba\x91\xfc\xe8\x99\xeb\x93\x93\x47\xfd\x25\x27\
    \xb4\x8f\xc5\xc5\xa5\x9f\x57\x6c\xfd\xa5\xcd\x69\x13\xb4\x81\x10\
    \x92\xce\x84\x60\xbd\x5c\xba\x03\xd4\x05\xf0\x9f\x7e\x96\xbb\xc0\
    \x55\x3f\xc6\x5a\xe4\xc0\x2e\x60\x0b\x0f\x87\x82\xbf\x6e\x22\x5a\
    \x97\x01\xf6\x00\x26\xf7\xa3\x0e\xbc\x23\x02\xa7\xfe\x87\x90\xc0\
    \xff\xb7\x80\x7f\x01\x46\x93\xdb\xb3\xb1\xe0\x54\xaf\x00\x00\x00\
    \x00\x49\x45\x4e\x44\xae\x42\x60\x82\
    \x00\x00\x07\x78\
    \x89\
    \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
    \x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\
    \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
    \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x03\x76\x00\x00\x03\x76\
    \x01\x7d\xd5\x82\xcc\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
    \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
    \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x06\xf5\x49\x44\
    \x41\x54\x78\xda\xc5\x97\x0b\x50\x94\x55\x14\xc7\xef\xee\xc2\xb2\
    \x0f\x76\x89\x87\xc2\x0a\xf2\x12\x11\x64\x17\xdd\x5d\x90\x47\x8a\
    \xb0\x22\xf2\x90\x1a\xb5\x14\x35\x32\x50\xac\x19\x42\x93\x45\x12\
    \xcb\xdc\x14\x5f\x15\x10\xd6\x64\x96\x0e\x92\xe5\x64\x86\x58\x88\
    \x4a\x21\x14\x34\x0a\xf8\xa8\x41\x11\x90\xe4\x55\xb4\xc2\x02\xc2\
    \x86\xf1\xfe\x4e\xe7\xce\xb4\x66\xad\xaf\x51\xa9\x3b\xf3\x9f\xef\
    \x7c\xf7\xbb\x97\xf3\xe3\x9e\x73\xee\xbd\x4b\x00\xe0\x7f\xd5\x43\
    \x4f\x74\x72\x72\x5a\xe0\xee\xee\xbe\x8f\xcb\xe5\x3e\x7b\x7b\x3f\
    \x6d\xbd\xf5\x99\x4f\xeb\x2f\xc5\x2f\x6f\x2e\x72\x75\xc4\xd7\xc7\
    \x07\x90\x98\x98\x68\x9a\x96\x96\xe6\x80\x7f\x33\x82\xc3\x62\x55\
    \xb2\x58\xac\x7e\xb4\x5b\x51\x2c\xfa\xbd\xea\x0d\xc2\x67\xea\x34\
    \x2d\xa0\x3d\x34\x08\xf5\x71\x7d\x50\x3b\x57\xcf\xd4\x4d\xff\x6d\
    \xa4\xc1\xb5\x4c\x5f\x6b\xbb\xe6\x6c\x21\x11\xdd\x17\xa0\x2b\xd5\
    \xdb\xa1\x53\xad\xf4\x30\x1a\x48\x88\x39\x4a\x8e\x4e\x67\x3c\xe7\
    \x6d\x13\xcf\x61\xb3\x68\x27\xb8\xb9\xb9\x81\x54\x2a\x5d\x00\x14\
    \x40\x43\xc6\x8f\xe6\x71\x2f\xc1\x49\xfe\x0d\x28\x26\xdd\x50\x8e\
    \x3a\x47\x7a\xe0\x12\x19\x80\xab\xa4\x9f\x69\xe1\x74\x0c\x68\xa7\
    \xe7\x5c\xad\xb6\x74\xba\x2b\x80\x4e\x2d\xff\xb8\x23\x59\x51\x64\
    \x78\x0f\x0b\x0b\x13\x49\x24\x12\x87\x29\x62\x32\xe5\x15\x29\x49\
    \x7d\x3b\x90\x64\x67\xcd\xe1\x67\x85\xf8\x78\xfd\xbc\x79\xf3\x66\
    \x68\x68\x68\x80\xec\xec\xec\x7a\xc3\xf8\x1f\x34\xc4\xee\x97\x7d\
    \x24\xf8\xb7\x4f\x49\xd4\x4f\x1f\x91\x59\x25\xbb\x49\x44\xe5\x27\
    \x64\xd7\x8d\xb3\xa4\x1e\x7e\x26\x83\xf0\xab\x09\xc3\xf4\xcc\x6f\
    \x2e\x29\x21\xc2\x3b\x02\x5c\x4f\xf1\x16\x1a\xec\x89\x22\xc2\x3f\
    \x1a\x4e\x56\x17\x87\xb3\x72\x4f\x86\x90\xac\x95\x6e\xe4\x79\xa5\
    \x52\xa9\x2a\x28\x28\x50\xad\x59\xb3\xe6\xf3\xd2\xd2\x52\x68\x6a\
    \x6a\x82\x6b\xd7\xae\x31\x4b\x96\x2c\xb1\xba\x57\xf8\xb2\x12\x89\
    \x59\xd9\x41\xb2\x09\x9a\xc8\x10\x74\xcd\x65\x06\xda\xed\x72\xc8\
    \xdd\x12\x44\xa7\x31\xb3\x6e\x49\x16\xc4\xb7\xaf\x35\x3d\x9d\x17\
    \xc5\xd6\x88\xb9\xc4\x3b\x28\x28\x48\xba\x6a\xd5\x2a\x15\x3a\x55\
    \x35\x36\x36\xaa\x2a\x2b\x2b\xa3\x0f\x1d\x3a\xc4\xd4\xd4\xd4\xc0\
    \xcd\x9b\x37\x61\xdd\xba\x75\xbb\xef\x9b\xf1\xd8\x74\x55\x64\x2f\
    \x5c\x9f\x09\xd0\xe9\xd0\x5b\x78\x98\xd8\x18\x0f\xcc\x26\x1f\x74\
    \x6f\x33\x1b\x68\x58\x2f\xfc\x30\xda\x4b\x38\x09\xe7\xd8\x6b\x96\
    \x85\xce\xac\xae\xae\x96\xb6\xb6\xb6\xca\x32\x33\x33\x17\xa2\xad\
    \xd2\xeb\xf5\xaa\x4d\x9b\x36\xb5\x54\x54\x54\x40\x4f\x4f\x0f\x0d\
    \x43\xf3\x83\x24\x72\xff\x05\x72\x04\xb4\x8b\x00\xda\xcc\xbb\x4a\
    \xbf\x26\xd6\x7f\x7f\xfc\x82\xf0\xe1\x80\x69\x0d\x1c\x74\x1c\xad\
    \xdc\x20\x5e\x84\x8e\x25\x22\x91\x48\x52\x9d\x16\x11\xd5\xae\x56\
    \x76\x68\x37\xcf\xcd\x69\x2f\xf8\x20\x6c\xef\xde\xbd\xd1\xc5\xc5\
    \xc5\x61\x6d\x6d\x6d\x2a\x8d\x46\x93\xb1\x6c\xd9\x32\xc0\x72\x04\
    \xb5\x5a\x3d\x8a\x73\x6c\xef\xe5\x5c\x77\x82\x04\x8e\xd6\x07\x0c\
    \x40\xbd\x18\x06\xea\xc8\x67\xb7\x42\xc0\x9c\x32\x17\x8c\xe4\x89\
    \x74\x70\x2a\x00\xe0\xa0\x45\x02\x76\xb3\x6d\x6c\x6c\xc6\x5f\xdc\
    \xbe\xc2\x57\x97\xa2\xbc\x8e\x89\x09\x1d\x29\xca\xee\xab\x97\x2f\
    \xcb\x7a\x7b\x7b\x65\x3b\x77\xee\x5c\x4e\xc3\x91\x9a\x9a\x3a\x07\
    \xc7\xb6\xa1\x60\xcb\xc6\xf5\xe0\x28\x24\x1b\xee\xe6\xbc\x27\x9f\
    \xac\x66\x2e\xfa\x8e\xc2\x8f\x12\x18\xbe\x48\xea\xf7\x6f\x25\x02\
    \x43\x58\x88\xf6\x88\x65\x2b\x94\x07\xc1\xc8\xe9\xe0\x2a\xc3\x84\
    \xb8\xb8\x38\x5e\x61\x61\xa1\x44\xbb\x35\x7a\x95\x6e\x83\xdf\x99\
    \xc6\xd4\x59\x9f\x55\xe5\xef\x0f\xc8\xcd\xcd\x95\x46\x45\x45\x85\
    \xe0\xb4\xc8\xc9\x93\x27\xab\x14\x13\x2d\xbf\xb4\xe5\x11\x88\x0c\
    \x0b\x85\xc0\xc0\xc0\x5a\xa3\xa4\xde\x43\xa6\x0d\x16\x38\x54\xc3\
    \x19\x7f\x06\xbe\x13\xc2\x50\x19\xb9\x9a\xbf\x83\x98\xdf\xca\x8b\
    \xe3\x7b\xa6\xcd\xea\x3e\x81\x09\x7c\xf9\x25\x18\x3a\x31\x29\xef\
    \xf6\xc9\x49\x49\x49\xe2\x8a\x63\x07\x9c\x5b\x6a\x6b\x27\x60\x9c\
    \x27\x60\x22\xfa\xa2\x63\x69\x6c\x6c\xac\x4c\x2c\x16\xc7\xa0\x3d\
    \x27\x3c\x3c\xfc\x05\x7c\x02\x86\x0b\xd2\xd3\xd3\x01\xed\xa9\x74\
    \xae\x76\x1b\x71\x1f\xf8\x58\x54\xc9\x9c\xf0\x63\xe0\x94\x37\x30\
    \x5f\xb1\x87\xfa\x8e\x91\xfd\x31\x31\x84\xfb\x8f\xc4\x3c\x97\x1f\
    \xb9\xf3\xf7\xd3\xd6\x00\xe7\x43\xa0\xbf\x64\xc6\xd1\x7f\xff\x07\
    \x18\x5b\x6b\x2c\x3b\x49\x64\x64\xa4\x64\xe3\xc6\x8d\x4e\xe8\xc0\
    \xd7\xd4\xd4\x54\x8a\x65\xa7\x42\xfb\x19\xb9\x5c\xae\xc2\xed\xb8\
    \x8e\x42\xa4\x25\x27\x41\x46\x04\xef\xc2\xf0\x36\x4e\x13\xb3\x47\
    \xc2\xc0\x41\x17\x60\x72\x84\xa3\x7f\xe4\x9a\x15\x55\xbc\x45\xc6\
    \xdd\xa9\xea\x48\xd9\xe1\x19\xbe\x6d\xe5\x4f\x8d\x42\xb9\x08\xa0\
    \xdc\x93\xe9\x3b\x25\xfd\xb0\xe6\x78\x82\x95\x61\x70\xc7\x7a\x5f\
    \xbb\xed\xa1\xce\xb4\x1a\x24\xb3\x67\xcf\xa6\x92\xa1\xad\xf0\xf1\
    \xf1\x91\xcd\xf7\xf3\x78\xf9\x2d\x85\xc9\xdb\xfb\xe6\x5a\xd4\x1d\
    \x9e\xc7\x82\xae\xb5\x42\xe8\x4b\x13\x02\xa4\xf3\x80\x79\xd7\x66\
    \x64\xe0\x3d\xcb\xf3\x0d\x5b\x88\x14\xc7\xdf\xfb\x2c\xb8\x90\x37\
    \x2d\x43\x7f\x71\xd1\x08\x9c\x53\x02\x14\x09\x80\x39\xc6\x65\x86\
    \x72\x05\xbd\x3d\xe9\x13\xb5\x9d\xa9\xb2\xfe\x1b\x6f\x38\xb7\x34\
    \xbe\x26\x28\xab\x53\x9b\x7f\xaf\x7d\xdd\xbe\xba\x29\x59\xd2\xad\
    \x4f\xb2\x18\x1a\x7e\x91\x07\x83\x4b\x09\x0c\xad\xe0\x42\xcf\x5a\
    \x7b\x68\x56\xbb\x40\xd5\x8e\xa7\xe1\xe8\x6a\xb7\x4f\xde\x5b\x28\
    \xe6\xa3\xdf\x07\x3f\x0d\x8b\xde\xe7\x78\xb6\x7e\x13\x78\x78\xf8\
    \x7c\x64\x2b\x53\x19\x31\x0c\x65\xf3\xa0\xfb\x75\x1f\xa0\x15\xd0\
    \x99\x22\x87\xfe\x0c\x77\xd0\x65\x4e\x81\x8e\x77\xbc\xa1\xef\xfd\
    \x20\xe8\xda\xe1\xcf\xdc\xdc\xea\x3b\xa8\x7d\xd5\x47\x7f\x25\xc1\
    \xb3\x25\x41\x6e\xf5\x0b\x87\x45\x00\x93\x97\x9e\x0f\x9f\x3f\xf4\
    \x71\xfc\x57\x63\x57\x67\x71\x17\x75\xa4\x79\xd5\x74\x6e\x98\xde\
    \xd6\xb5\xcd\xb7\xaa\x2f\x47\xba\x5b\x97\xe1\xb0\xa7\x3c\xe9\x89\
    \x37\xf7\x2f\xe4\x3d\x15\x15\xaa\x52\x62\x2e\xcc\xf2\xf0\xf0\x08\
    \x75\x71\x71\x89\xc7\xb2\xdd\x8e\xf3\x40\xa1\x50\xc0\xe2\xc5\x8b\
    \xff\x40\x9b\xff\x78\xee\x03\x84\xb0\xb4\x49\xf2\x71\xb7\x01\x0a\
    \x51\x12\x4f\x4f\xcf\x09\x7e\x7e\x7e\x4f\xa2\x1d\x80\x1b\x51\xac\
    \x40\x20\x88\x67\xb3\xd9\x7d\x14\x02\xab\x07\xf0\xd4\x5c\x3a\x66\
    \x17\x12\x6c\x16\x14\x02\x2b\x60\x92\x97\x97\x57\x08\x96\x64\x14\
    \x42\xac\xe4\xf1\x78\xdf\x52\x80\xe8\xe8\x68\xc0\xf7\x92\xb1\x04\
    \x60\xa1\xac\x29\x84\x4c\x26\x9b\xee\xe8\xe8\xa8\xb2\xb3\xb3\x8b\
    \x33\x37\x37\xdf\x45\x01\x9c\x9d\x9d\x21\x22\x22\xe2\xd6\xd6\x7c\
    \x6b\x05\x35\xc1\x26\x0f\x0f\x60\x0c\xc1\x46\x8d\x47\xd9\x63\x1e\
    \xcc\x44\xe7\x8b\x11\x24\x01\xc3\xa0\xc3\x3e\x9a\x07\xc0\xe1\x70\
    \xd6\xfd\x75\xc7\xb8\x81\x2a\x45\x9d\x7f\x34\x00\x63\x08\x53\x94\
    \x1d\x9f\xcf\x77\xc3\x36\x47\x28\x14\x26\x9a\x99\x99\x1d\xa7\x00\
    \xfe\xfe\xfe\x80\xf7\xc6\x4b\x5d\x29\xf2\x00\x74\x3c\x88\x02\x54\
    \x6d\x93\x26\x98\x67\x04\xf0\x88\x10\x7c\x94\x04\xf3\x40\x81\x37\
    \xa7\x18\x5c\x89\xad\x86\xad\x19\xcf\x06\xd8\x37\xdf\xe5\x4d\x5d\
    \x8a\xfc\x0a\x05\xa0\xcf\x76\xb5\x62\x9e\x31\xc0\xa3\x43\x88\x51\
    \x0e\x96\x96\x96\xc1\x16\x16\x16\x6b\x31\x0c\xcd\x14\x22\x38\x38\
    \x98\x86\x61\x97\x2e\x79\x9a\xbb\x2e\x59\x7e\xcc\x30\x7e\x2c\x00\
    \x68\xb3\x42\xb9\x23\xc0\x52\x0c\x49\x3e\x05\x70\x75\x75\x05\x5b\
    \x5b\xdb\x36\x9a\x2f\x0f\x5a\x05\x8f\x9a\x94\xe3\x50\xfe\x18\x86\
    \xd7\x70\x15\x46\x29\x04\x96\x29\xfd\xa8\x1a\x6b\x00\x03\x84\x09\
    \xca\x1e\x15\x83\x27\xe4\x15\x0a\x30\x75\xea\x54\xba\x29\x1d\x18\
    \x3b\x00\x63\x08\x33\x94\x8c\x3a\x35\x31\x31\x01\xdc\x21\x01\x57\
    \x84\xee\x90\x82\xb1\x05\x30\xfe\x11\xb3\x04\x21\xfa\x31\x14\x60\
    \x65\x65\x45\x3b\x63\xc7\x16\xc0\x18\x62\x02\xea\x0c\x42\xd0\x55\
    \xa0\x1d\x67\xff\x6b\x00\x16\x2a\x19\x05\x14\x02\x9f\x23\x18\x12\
    \xe1\xd8\x02\x18\x43\x08\x50\x0d\xb8\x17\xb4\xe0\x15\x2e\xfb\x4e\
    \x63\xfe\x04\xc1\x02\x5a\xcc\xca\x82\xdb\xf8\x00\x00\x00\x00\x49\
    \x45\x4e\x44\xae\x42\x60\x82\
    \x00\x00\x06\xe0\
    \x89\
    \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
    \x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\
    \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x1b\xaf\x00\x00\x1b\xaf\
    \x01\x5e\x1a\x91\x1c\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xd7\x0c\
    \x1b\x16\x05\x11\x8e\x6b\xb0\xdd\x00\x00\x00\x06\x62\x4b\x47\x44\
    \x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x06\x6d\x49\x44\
    \x41\x54\x78\xda\xcd\x57\x5b\x6c\x54\x55\x14\x5d\xf7\xde\x99\xb9\
    \xf3\xe8\xf4\x01\x08\x04\x8a\x52\x3e\x4a\x55\x62\xa6\x10\xc4\x86\
    \xa2\x12\x22\x90\x48\x48\x30\x8d\xa1\xbc\x0a\x82\x3c\x52\xa8\x06\
    \x13\xd0\x0f\xe5\xcb\x98\x88\x06\x43\x0c\xef\xc4\x2f\xf8\x20\x24\
    \x02\xf1\xc3\x47\x08\x2a\x4a\x30\x80\x96\xd2\x82\xb4\xb4\xbc\x9f\
    \xe9\xcb\x4e\x87\x99\x7b\xe7\x3e\xdc\xfb\xf4\xe6\xdc\x19\x5b\x22\
    \x1f\x10\xdd\xc9\xea\x39\x3d\x67\x9f\xbd\xd6\xde\xfb\xcc\x99\x0c\
    \xfe\x6b\x53\xe0\x59\x2b\xcd\x8b\xc3\xe1\x90\x13\x8f\xeb\x41\x4d\
    \x53\x95\xc7\x4c\xe4\x12\xac\x68\xd4\x55\x3a\x3b\x33\xbd\x7d\x7d\
    \x66\x39\x2d\x49\x01\xbd\x34\x5a\x75\x75\x09\x65\xd4\xa8\x7d\xbd\
    \x27\x4f\x26\xb2\xf7\xef\xab\xde\xa1\xfc\x20\x72\x1c\x7a\x4d\xce\
    \x1f\xb2\x17\x9b\x30\xc1\x1d\x5e\x5d\xdd\x68\x36\x35\xad\x8c\x1e\
    \x3c\xd8\x58\x4c\xcb\x01\x76\x48\xeb\xba\x1e\x28\x2d\xdd\x17\x98\
    \x3d\x7b\xb2\x56\x55\x05\x55\x51\x06\x67\xf0\x38\xca\xad\x52\xe4\
    \xc2\xc2\xca\xcc\xb5\x6b\x7b\x15\x5d\xaf\x86\x61\x64\x84\x80\xac\
    \xaa\x46\x6e\x1d\x39\x92\x88\x57\x54\xc0\x34\x4d\x3c\x49\x73\x28\
    \xb9\xd6\x63\xc7\x2a\x9f\x27\x4e\x00\x03\x02\x6c\xd7\xd5\x7a\x9a\
    \x9b\x55\x3d\x99\x84\x8d\x27\x6b\xb6\x61\xa0\x8f\x5a\xcc\x9c\x20\
    \x1b\xa8\x80\xeb\xaa\x0e\x00\x2b\x93\xc9\x13\xd0\x4d\xce\x6d\xe1\
    \x30\x82\xe9\x34\x12\xba\xce\xad\x79\xb4\x2c\x5d\x17\x8d\x74\x36\
    \x1b\x89\xa0\x9c\x62\x96\xe8\xba\x2f\x20\x9b\xe5\x84\x05\xa7\x14\
    \x40\xe4\xaa\xcd\x9b\x39\x02\xfe\xb2\x6d\x38\x89\x04\x16\x55\x57\
    \x23\x4d\x02\x7e\x3c\x74\x08\x65\xc9\x24\x8b\xf8\x57\xf2\x2b\xf1\
    \x38\xe6\xd5\xd6\x22\x42\x02\x7e\x3b\x71\x02\x3d\xe7\xce\xa1\x50\
    \xd3\x06\xf6\x4d\x93\xdb\x20\x38\x01\xef\x8f\xe3\x38\x2c\x80\x2b\
    \x20\x11\xa5\xcb\x38\x8d\xc8\x15\x72\x8e\x46\xa3\x78\xb5\xa6\x06\
    \xb7\x4a\x4a\x90\xe5\xfd\xa1\x21\xf6\x6e\x16\x17\xb3\x2f\x9f\x11\
    \x67\xa7\xcd\x98\x81\x48\x55\x95\xf4\x71\xb8\x02\x1e\xa7\x14\x60\
    \x3b\x8e\xdf\x02\x0f\x65\x45\x45\x50\x55\xb1\x0d\x97\xb2\xd2\xa9\
    \x8c\x2f\xcd\x9f\x8f\xae\xd2\x52\xe9\x27\xe1\x91\x77\x8e\x1d\x2b\
    \x7c\x42\xa1\x10\x13\x80\x8d\x63\x94\x91\x28\xe9\xcb\x15\xf0\x38\
    \xa5\x00\xcb\x6f\x81\x54\xda\x7a\xe0\x00\x1e\xb4\xb7\xc3\xab\x90\
    \x40\x30\x18\x44\xe5\xdc\xb9\x48\x4d\x9c\x08\xcb\x30\x64\x50\x9e\
    \xa7\xca\xcb\x91\x98\x33\x07\x81\x40\x00\xb6\x6d\x33\x84\xf0\x07\
    \x1d\x1d\x68\xdb\xbf\x3f\xaf\x02\xdc\x02\xe6\xf4\x2b\xe0\x5f\x42\
    \x89\x6c\x2a\x85\x96\xbd\x7b\x91\x6a\x6b\x93\x22\x38\x20\x13\x3c\
    \x37\x6b\x16\x9c\x29\x95\x50\x6c\x43\xc0\x9e\x9c\x40\xc5\xcc\x99\
    \xd0\x34\x2d\x9f\xfc\xf2\x65\xb4\xec\xd9\x03\x33\x95\x1a\x24\xc0\
    \xce\xbd\x84\x36\xa0\x88\x45\xc3\xe0\x0d\xff\xc6\x12\xce\xef\xd8\
    \x81\x49\x6b\xd6\x20\x58\x56\x26\x2b\x21\xca\x3a\x7d\x06\x6e\x93\
    \x18\xc0\x45\xe9\x8b\x55\xbc\xc6\xc4\x3c\x8a\xde\x1b\x1d\xed\xb8\
    \xb0\x7b\x0f\x1c\x5a\xcb\x35\xdb\x13\xc0\x9c\x42\x80\xac\x00\x07\
    \x30\x4d\x11\x44\x1a\xb9\xd0\x32\x2e\xee\xdc\x8e\x67\xd7\xd6\x43\
    \x7d\xa6\x4c\x56\x82\x89\x46\x4f\x9d\x06\xcf\x64\xcf\x99\xdc\xb9\
    \x76\x05\x97\x76\xef\x80\x63\x5a\xd4\xeb\x9c\x67\x94\xf6\xf4\xe2\
    \x22\xe2\x73\x04\xa7\x7f\x07\x7c\x01\x7e\x1b\x8c\x0c\x5c\x33\x03\
    \x35\x9b\x81\x92\xe9\x47\xfb\xce\xcf\xe1\x5e\x6d\x63\x02\x59\x09\
    \xef\x82\xf2\x5c\x0a\xd7\x6e\x5e\x41\xc7\xae\x6d\x80\xd1\x0f\xc5\
    \xca\x50\xcb\x34\xc4\xc7\x8d\x43\x64\xc4\x08\xb8\xe4\x77\x66\xeb\
    \x56\x98\x19\x03\xd6\x3f\xdf\x01\x57\x55\xd0\x7d\xa1\x05\x4a\x38\
    \x82\x50\x3c\x8e\x60\x38\x84\x80\x4e\xe5\x74\x07\x54\x2a\x06\x70\
    \x7d\xd7\xa7\x78\x7a\xf5\x46\xb8\x63\x64\x3b\x58\x00\x43\x08\x8b\
    \x27\xbb\x71\xf7\xf0\x7e\xe8\x31\x1d\x0a\x47\x4d\x1b\xe8\xbb\x7e\
    \x19\x1d\x47\x0f\x23\x9d\x4c\x83\x2d\x18\x8b\x82\xb8\x78\x37\x4f\
    \x80\xc2\x15\x48\xde\xb8\x8e\x4c\xff\x03\x68\x9a\x82\x48\x2c\x84\
    \x58\x61\x04\x91\x02\x1d\xe1\x68\x08\x7a\x38\x08\x27\x12\xc2\xb1\
    \x1f\xbe\x47\xd5\x92\x55\x92\x38\x57\xc4\xaf\xdf\x7e\x83\xe8\xd7\
    \x5f\x51\xf5\x2c\x98\x59\xc0\xf0\xa0\x10\x34\xa2\xb3\x99\x88\xc9\
    \x5d\x55\x70\x4a\x01\x42\x84\xea\x35\x1c\xec\x4c\x3d\xb6\x0d\xb8\
    \x29\x03\x36\x1d\xb6\xfa\xc9\x5b\xd7\xd0\xf6\xda\x22\x54\xd7\x2c\
    \x91\x6d\xc8\x05\x5b\xd9\xdc\x05\x68\xee\xba\x8f\x91\x47\xf7\x01\
    \x60\x61\x10\xe0\x6d\xee\x10\x8f\x8a\x46\xe4\x39\x17\x5d\xcd\xfd\
    \x96\x62\x75\xac\x21\xa0\xf9\x08\xf2\x18\xd2\x70\xe7\xf5\xe5\x78\
    \xa5\xe1\x03\xc4\x62\x31\x78\x26\x6f\x3c\x67\xcf\x77\x80\x3f\x86\
    \x2f\x2c\x5d\x87\xee\x37\xd6\x8a\x33\xc1\x40\x7e\x2c\x72\x17\x1c\
    \xae\xa2\x0c\x16\xc0\x7d\x61\x75\x1a\xed\x69\x39\x22\x34\x0a\xd4\
    \xb5\x60\x35\x5e\xde\xf0\x3e\x0a\x0a\x0a\x98\x50\x92\x0f\xbf\xd1\
    \x88\xa7\x6e\x36\xf2\x5c\xb6\x83\xdf\x89\x44\x5d\x3d\x52\xb5\x0d\
    \x7c\xd6\x8f\xc3\x50\x21\x7c\x1d\x75\x68\x01\xb2\x02\xc2\x91\x41\
    \xe9\xa7\x16\xae\x27\xf2\xcd\x9c\x39\x93\x33\x38\x53\x22\x3f\x0b\
    \xfb\x8b\x65\x70\xb6\xaf\xc0\xe8\x3b\x4d\xfc\x4a\x32\x39\xef\x89\
    \x67\x7b\xca\x5b\x1b\xe0\xae\xd8\xc4\x31\xf2\x62\x2a\x94\xa1\x3b\
    \x94\x00\x56\xc5\x9b\x14\x5f\x40\x0d\x68\x14\x60\x23\x91\x6f\x62\
    \x72\x56\xce\x10\x24\xc5\x57\x4f\x0b\xf2\xb0\x6a\x0a\x28\x5f\xae\
    \xc0\x98\x7b\xe7\x11\x0e\x87\xa5\x10\x9e\x4f\x7d\xbb\x01\x91\xfa\
    \x0f\x45\x2c\x11\x93\xa1\x29\xc4\x85\x21\xef\x00\x11\xf8\x02\x62\
    \xeb\x36\xa3\xba\xfe\x3d\x49\xce\x99\x71\xe0\x58\xdb\x29\x64\x3e\
    \x5b\x82\xa0\x6b\x42\x0f\x01\x61\x42\x10\x26\xb2\xdb\x96\x53\x55\
    \xfe\x18\x2c\x62\xd5\x7a\x0c\x7b\x77\x8b\x88\x09\x16\xf0\xf0\x3b\
    \xc0\xff\xf9\x1b\x55\x8b\xeb\x44\x00\x32\x49\x1e\x6c\xf9\x05\xbd\
    \x1f\x2f\x02\xb2\x26\x06\x99\x65\x22\xf9\xc9\x52\x44\x5b\x4f\x71\
    \x0b\x58\x04\xb7\x4b\x9c\xab\xac\x59\x28\xdd\x14\x55\xe0\x61\x97\
    \x10\xf2\xa3\x73\x7a\x65\x0d\xd0\xdf\x27\x9f\x57\xfb\xf7\xe3\xb8\
    \xfd\x51\x2d\x6c\xc3\x14\x9f\xe7\xac\x05\xd0\x14\x19\x02\xcf\x6d\
    \x1b\xe2\x25\xbd\xbb\x65\x31\x70\xee\x67\xf9\x55\xce\x31\xce\x50\
    \x2c\x8e\x09\x86\x77\xd7\x86\x68\x81\x20\x92\x02\x7a\x5a\xce\xe3\
    \xcc\xb2\x79\x50\x93\xbd\x48\x9f\xfc\x0e\xed\x9b\x16\xc2\x22\xb6\
    \xac\x0d\xf9\xc8\xa4\x0d\x21\x40\xc0\xb4\x40\x7b\x04\x52\xd5\x41\
    \xbe\x7c\x86\xcf\x9e\xad\x9b\x47\xb1\x9a\x44\x4c\xc7\xf5\xb2\xf7\
    \xf9\x11\xc8\x6f\x81\x70\x12\x19\x3a\x04\x3e\xf8\xd3\xf4\xf1\x88\
    \x84\x20\xfa\xad\x12\xbc\xf6\x09\x3f\xcd\x93\xcf\xfe\x96\xc5\x22\
    \xbc\x91\x2a\xd1\xdc\xf0\xe6\x80\x38\x83\x63\xf9\x31\x21\x12\xcd\
    \x6f\x01\xaf\xd9\x6a\xc1\x30\x37\x3a\xb2\x90\x1d\x05\x2c\xdb\x47\
    \xd6\x62\x88\xcc\x05\x0c\x2f\x70\xda\x43\x86\xc1\xeb\xde\x3e\xfb\
    \x5a\x02\x32\x86\x8c\x1b\x1d\x59\x44\xeb\x8a\xc3\x9c\xb2\x02\x2e\
    \xd0\xad\xc7\xe2\x97\xe2\x93\xc6\x57\x68\xd1\x10\x34\xc7\xe2\x57\
    \xcc\x7b\xc9\xfc\x51\x23\x28\x04\xae\x96\x45\x90\x99\x70\xdb\xf8\
    \x99\x25\xa8\x84\xa0\x4d\x73\x8b\xce\xd0\xa8\xd3\x18\xf5\x12\xb0\
    \xb5\x20\xac\x78\x09\x94\x0b\xf7\x2e\x31\xa7\x14\xa0\x00\x3d\xa1\
    \x8b\x7f\x36\x74\xf4\xf6\x6c\xef\xe9\xea\x2c\xcf\xa6\xd3\xea\xa3\
    \xff\x0c\x7b\x74\x0b\x45\xa2\x4e\x58\xbd\x71\x69\x74\xb2\xff\x1d\
    \xe6\x44\xee\x75\x38\x3e\xd0\x8e\x12\xc2\x30\x6e\x2f\x9e\x80\x79\
    \x65\xe7\xcc\x7b\x66\x02\x0e\xfe\x0f\xf6\x37\x83\x76\xd2\x44\xe2\
    \x1d\x68\x05\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
    \x00\x00\x08\x9f\
    \x89\
    \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
    \x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\
    \x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\
    \xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x1b\xaf\x00\x00\
    \x1b\xaf\x01\x5e\x1a\x91\x1c\x00\x00\x00\x07\x74\x49\x4d\x45\x07\
    \xd7\x0a\x1a\x12\x28\x08\xfa\xdf\x78\xeb\x00\x00\x08\x2c\x49\x44\
    \x41\x54\x78\xda\xd5\x97\x6b\x6c\x5b\xe5\x19\xc7\x7f\xef\x39\xbe\
    \x1c\x3b\x76\xe2\x38\x76\x6e\x8e\x9a\xf4\x42\xd3\xa5\x6b\xc7\x04\
    \x4d\xa1\x05\x2a\x51\x18\x1d\xdd\x46\x57\xbe\x74\x90\x95\x8e\x69\
    \x68\x1f\x98\x34\x89\x6e\xd3\x90\x86\x26\xa4\x7d\xd8\x26\x31\xed\
    \xc6\x26\x6d\x93\x28\x63\x2b\x6d\x29\x1b\x5a\x27\x18\x90\x34\xd0\
    \x74\x85\x36\x85\x94\xa6\x97\x84\x24\x4d\x5c\xe7\xee\x5c\x9c\xf8\
    \x76\x7c\x6e\x3b\x3d\x09\x51\x53\x35\x5c\xc4\xa4\x69\x7f\x9d\x47\
    \xaf\x2d\xdb\xcf\xef\xef\xe7\x79\xcf\xa3\xf7\xf0\xbf\x96\xe0\x13\
    \x68\x0f\xb0\xa3\xb2\xb2\xac\xba\xb1\xf1\xd6\xe2\x75\xeb\xea\x03\
    \x0d\x0d\x11\x4f\x34\xea\xc5\xb2\x66\xb3\xfd\xfd\xfd\xd3\x9d\x9d\
    \x1d\xaf\x3f\xf3\xcc\x7b\x7b\x67\x67\x0d\x3e\xa6\x5c\x7c\x0c\xfd\
    \x11\x3c\xe5\xf0\x80\xbf\xba\xfa\xe1\xe8\xdd\x77\x6f\x2a\xb9\xe9\
    \x26\xd9\x53\x5e\x8e\x2b\x18\x44\x92\x24\xb0\xc3\x57\x53\x83\xec\
    \xf1\x70\x4f\x3e\x3f\x76\xeb\xd9\xb3\x07\xa7\x7a\x7a\x7e\xb3\x2f\
    \x99\xec\x3a\xf4\x69\x2a\x70\x3f\x88\x07\x61\x87\x12\x08\x3c\x55\
    \xbe\xed\x9e\xba\xf0\xe6\xdb\x90\xdc\x6e\xcc\x42\x01\xcb\xb2\x9c\
    \xe0\xca\x6a\x9a\x73\xaf\x01\x57\x71\x90\x5c\xfc\x32\x63\x2f\x1c\
    \x32\xd2\x3d\xbd\xcf\x66\xe0\x07\xbb\x60\xfc\x13\x1b\xf8\x13\xf8\
    \x43\xf0\x74\xf4\xb6\x4d\x0f\x55\xed\xd8\x01\x26\x73\xb0\xab\xc2\
    \x26\xdb\xd7\x82\x91\x45\xa6\x84\xe2\x25\x7b\xf1\x02\xa3\x87\x5f\
    \x1c\xce\x65\xb2\x4d\xe7\xa0\xe5\xc7\x1f\xd7\xc0\x1f\x20\x14\x92\
    \xe5\x23\xb5\x4d\xbb\x36\x97\x34\xac\xc3\xd4\xf4\x05\x80\x3a\x3d\
    \x41\xfa\xe2\x45\x72\x89\x04\x7a\x6a\x06\xcb\xd0\x91\x14\x1f\x9e\
    \x68\x04\x5f\x5d\x1d\xbe\x65\xb5\x80\xb4\x60\x46\x58\x06\x23\xfb\
    \xff\x5a\x98\x1d\x1c\xd9\xf3\x24\xec\x3f\xf7\x51\x06\x9e\x02\xa5\
    \x4a\x96\x5e\x59\xf1\x8d\xa6\x2d\x45\x35\xcb\xb1\xff\xa1\x03\xcf\
    \x4f\x8e\x31\xd6\xd2\x42\xaa\xab\xa7\x4f\x37\xad\xc3\x05\x38\x9e\
    \x81\x4b\x06\x68\x7e\x88\x28\x70\xa3\x1b\xb6\x2b\xc5\x45\x77\x96\
    \x6c\xb8\xd9\x1d\x5c\xbb\x1e\x10\x8e\x09\xc9\x25\x18\x3d\x74\x48\
    \x4b\x25\x86\x77\x36\xc1\x91\x25\x0d\x6c\x02\x1e\x81\x5f\xd5\xde\
    \xb7\xed\x3b\xa5\x6b\x3f\xbf\x00\x9f\x3c\xdb\xce\xf0\x6b\x6f\x0c\
    \x67\x34\xfd\x87\xc7\x60\xff\x73\x50\xe0\x3a\xda\x09\x62\x2b\x6c\
    \x2f\x73\xcb\x2f\x95\xdf\xb7\x5d\xf2\x94\x55\x39\x06\x70\x4c\xc0\
    \xd0\x5f\xf6\x4f\x4f\xcf\xa4\x6f\xfe\x16\xf4\x32\x2f\x99\xab\xf4\
    \x38\xdc\x51\xb6\x66\xc5\xd3\x15\x9b\xb7\x08\xa3\xa0\x63\xea\x3a\
    \xa3\x27\xdf\x24\xd1\xdc\xf6\xef\xb8\x61\xde\xf5\x18\xb4\xbd\x07\
    \x06\x4b\xe8\xbb\xb0\x2a\xe8\x71\x1d\x88\xde\x73\x67\xc8\x55\x5a\
    \x89\xa1\x3b\x39\x9c\x55\xb7\xf3\xf9\x57\xd4\x28\xd9\xf3\x17\x3f\
    \xa3\xc1\x73\xdd\xd7\x1a\xd8\x0a\x62\xbd\x2c\x1d\xac\xfd\xea\x97\
    \x6a\x2c\x53\x76\x7e\x38\xd5\xdb\xc9\xe5\xe6\xb6\x53\xdd\x16\xdb\
    \x7e\x02\x49\x3e\x44\x07\x1b\x1b\x6f\xf0\x62\xb4\x44\x37\x6f\x58\
    \xe6\x09\x55\xcc\x83\x17\x07\x1e\x05\xcc\xfc\xca\xd8\xd8\xe4\xc9\
    \x23\xf0\xfe\xa2\x39\xb0\x1d\xb6\x94\x34\xac\x68\x44\x28\xe8\xaa\
    \x8a\xae\xa5\x49\xb4\x1e\x9f\x19\xb2\xd8\xf5\x73\x48\xf1\x21\x7a\
    \x65\xcf\x9e\xd5\xc1\xa0\xbf\x99\x99\xba\x1a\x2b\x6b\x61\xa8\xea\
    \xc2\xa6\x35\x30\x51\x67\x47\x71\xf9\xcb\xd1\x72\x39\x02\x9f\x5b\
    \x47\xf2\xdc\xfb\xdf\xbb\xd5\xe2\xe5\x13\x60\x49\x57\xf5\xe2\xa1\
    \xc0\xca\xe5\xe8\x79\x15\x4d\x55\x19\xef\xec\x20\x95\x51\x7f\xd6\
    \x0a\x7d\x7c\x88\x5a\xf6\xee\xad\x2f\x6f\x58\xdd\xe2\xd1\xd2\x35\
    \x62\x5a\x05\x75\x21\x10\x7a\x81\xe9\x0b\xef\x30\x76\xec\x94\xa9\
    \xa9\x33\x4e\x5e\xc3\xf2\xa0\xc4\x22\x77\x7c\x19\xea\x00\x24\x80\
    \xdb\x41\x16\x7e\xf7\xdd\xee\x60\xc8\xf9\x92\xa6\x65\x19\xef\xee\
    \xcb\x5e\x82\xdf\xb7\xb2\xb4\x5a\x9f\x78\xa2\xbe\xc2\x86\x4b\xf1\
    \xee\x98\x35\x30\x8e\xb8\x02\x9d\x87\x4b\x96\x4e\xb2\xaf\x93\xa1\
    \x0b\xf1\xe7\x53\x79\xfd\x77\xd9\x91\x04\x7a\xa1\xc0\xcc\xe0\x30\
    \xc5\x6b\xd7\x48\x0a\x7c\x61\xc1\xc0\x2d\x50\xa3\x44\x8b\x63\x5a\
    \xd6\x70\x0c\x64\xa7\x47\x98\x9d\x55\xdf\x78\x03\x26\x96\x2c\xfb\
    \x63\x8f\xd5\x97\xd5\xc5\x5a\x8c\x77\x8e\x57\x1b\x17\x2e\x3b\x60\
    \x69\xde\x80\x4b\x86\xe1\x44\x17\xf1\xce\xfe\x03\x6f\xc3\x43\x19\
    \xd8\x9f\x9d\x48\xa2\xa9\x79\xb4\x7c\x1e\x77\x59\x18\x0b\x36\x2e\
    \xec\x01\x05\x56\x49\x45\x3e\xfb\xc3\xb9\xde\x65\xa7\x26\x29\x58\
    \xb4\x9f\xe5\xfa\x7a\x61\xf7\xee\xfa\xd2\x8a\x70\x33\x6f\xb7\x56\
    \x9b\xe7\x13\x48\xce\xc0\x99\xbf\xdd\x8a\xfd\xf4\x8e\xf4\xd2\x77\
    \xe6\xd2\xc1\xd3\xb0\xfb\x45\x28\x7c\x05\x3a\xc2\x33\x99\x42\x38\
    \x5c\xe2\xc9\xa9\x06\xf1\x33\x1d\x18\x82\x55\x1b\xad\x79\x03\x26\
    \x84\x2d\x59\x42\x9f\xdf\x3c\x85\x7c\x0e\x0d\x06\xaf\x3b\x25\xb7\
    \x6e\xad\x2f\x2e\x0d\x34\x2b\x9d\x27\x63\x74\x8d\x20\xcf\x6f\x36\
    \xd3\x0e\xb9\xac\x84\xf3\xc9\x7e\xba\xde\xed\x3d\x74\x05\xfe\x0f\
    \x28\x00\x8c\x41\x3e\x57\x30\x26\x2e\xb5\x1d\xad\x4a\xa7\x0a\x28\
    \xa5\x6e\x2c\x59\x94\x9a\xba\x25\x1c\x03\x1a\x88\xf4\x4c\x8a\xfc\
    \x74\x17\x7e\xbf\x1f\x5d\x38\x06\x04\xd7\xe8\xfb\x42\xac\xd6\xc6\
    \x12\xcd\x65\x83\xc4\xdc\xfd\x29\x24\x21\x10\x6e\x37\xb8\x5c\x98\
    \x3e\x37\x1d\x93\x03\x74\xb6\xf7\x1c\xea\x80\xaf\xff\x13\x54\xe6\
    \x65\x80\xa5\x03\x1e\xcb\x44\x02\x90\x84\x93\xdf\xf8\xa0\x05\x2a\
    \x4c\xab\x05\x95\xd4\xf0\x65\x32\x80\xaf\x4a\xc1\x84\xd8\x35\x43\
    \x66\x75\x91\x22\x35\xaf\xca\x8c\xc6\xa4\xa3\x49\xf4\x2b\xe5\x36\
    \x4d\x67\xc3\xe9\x56\x81\x8e\x32\x37\x17\x06\xf3\x2f\xbc\x63\xc3\
    \x5f\x03\x95\xab\x54\x04\x5e\xc9\x2b\x95\x89\xbc\x81\x04\x48\xb2\
    \x40\x33\xad\x54\x1e\x2c\xc7\xc0\x24\xf4\x46\x2d\x90\x25\x81\x64\
    \x5a\x78\x04\x18\x82\xc6\x52\x0b\xa6\x80\x47\xa1\x3e\xe8\x97\x9b\
    \x37\x84\x3d\xb1\xd2\xbe\x69\xa4\x39\xe7\xce\x6a\xa7\xe6\x74\xb9\
    \x97\xf7\x12\xb9\xc3\x36\xbc\xa9\x15\x54\xae\xd1\x7a\xfb\x72\xfb\
    \x64\x0f\x29\x0d\x19\x10\xb2\x40\x35\xe9\x3d\x3f\x9f\x87\x76\x88\
    \x57\xce\x6a\x63\x15\xa5\xee\x72\x31\x51\x40\x9e\xd5\xf1\x85\xdc\
    \x5b\xb6\x4d\x69\x91\x62\x28\x0b\xd8\xf0\x8d\x11\x6f\xac\x2c\x9e\
    \xc5\x0d\x78\xc0\x59\x65\x45\xe2\xcd\x0a\x85\xf6\x78\xf6\xf0\xbb\
    \x16\x4d\x6d\xa0\x72\x1d\xc9\xb0\xd3\xef\x12\xe8\x16\x48\x80\xdd\
    \x7f\xf2\x0e\x76\xee\x3d\x17\x40\x9b\xc9\x18\x47\xa5\x80\x0b\x01\
    \x18\xb6\x81\x68\x89\xdb\x1b\x82\x9f\x06\x8b\xe4\xe6\x5b\xca\xbd\
    \xb1\x90\x0d\x77\x39\xe0\xf9\xb8\x02\xaf\x52\x38\x11\xcf\xbe\x78\
    \x6a\x0e\x9e\xe7\x3a\xda\x05\x25\xde\xa0\xeb\xe1\xa2\xdc\x5c\xf9\
    \xdd\x5e\x89\x9c\x6e\x5a\x13\xf0\xea\x82\x01\x80\x34\xfc\x79\x16\
    \x90\x65\x81\x04\x44\xb2\x06\xe1\x2a\xe5\xe1\x5b\x2a\xbc\xb1\xe2\
    \xfe\x2c\x12\x38\x21\x03\x2e\x9f\x4c\x4b\xcc\xc7\xb1\xfe\xec\xdf\
    \xda\x2d\x1e\x7c\x6b\x09\xf8\x4a\x20\x04\x4f\x2e\x2b\xf7\x46\xcc\
    \x64\x01\x01\x78\x2b\x15\x46\xc7\x0b\x27\x4f\x40\xf7\x22\x03\x6d\
    \xf0\xaf\x81\xa1\x5c\xa7\xa7\xd6\x8f\x00\xcc\x31\x95\x46\x09\xfc\
    \x7d\x59\x04\x20\x7d\x10\x36\xfc\xf5\x1a\x1f\xad\x7d\x19\x1b\xce\
    \x03\xed\x4b\xc0\xe7\x27\xec\xae\xaa\x98\xef\xd1\xa8\x9d\x0b\x40\
    \x76\x4b\x4c\x0a\x98\x31\xac\x5f\xf4\x81\xb9\xc8\xc0\x00\xe8\x93\
    \x9a\xf5\x78\xdc\x2e\x8f\x2b\xe0\x02\x40\x1b\x5c\x9c\x5b\xf8\x65\
    \x5a\x96\xf9\x68\xe9\x49\xff\xfd\xb4\x0d\xef\x58\x02\x1e\x01\xd1\
    \x04\x7b\x2a\x2b\xbd\xfb\x6e\x34\x2c\xc9\x9c\xd5\x11\x80\x58\xee\
    \xa7\xef\x72\xee\xad\x36\x38\x7c\xdd\xf3\xc0\x24\xf4\x04\x52\xfa\
    \xaa\xe8\x0d\x45\xeb\x03\x93\x1a\xae\xf9\x49\xe5\xf4\xde\x86\x9f\
    \xb0\xab\x73\xb4\x2b\xfd\xd2\x19\x8b\xaf\x9d\x5f\x02\xbe\x13\x6a\
    \xd7\x08\x7e\xbb\x66\x45\xd1\x8f\x6e\xd3\x4c\xd9\x18\x51\xd1\x00\
    \xec\xaa\x9d\x4e\xe9\xd9\x44\xd6\xd8\x79\x0a\x86\x96\x3c\x92\x6d\
    \x80\x50\xad\x4b\x1c\xbd\x6b\x6d\xf1\x8d\x91\xce\x19\x14\xc3\xc2\
    \x07\x78\xed\x04\x27\x83\x2e\xde\x8d\xe7\xfa\x4b\xaa\x97\x3f\x5f\
    \x55\x5f\x7f\x5c\x17\xa2\x57\x18\x46\xa1\x38\x1a\x8d\x48\x96\xb5\
    \x7e\x2a\x71\xf9\x5e\x6d\xec\xe2\xbd\xeb\xcd\xb4\xa7\x6a\x20\x4b\
    \x36\x63\x90\x03\xb4\x4a\x85\x13\x2e\x61\x76\x27\x72\xdf\x3c\x02\
    \xcf\x7c\xe4\xa1\x74\x13\xc4\xca\x3d\xd2\x2b\x77\x35\x04\x3f\x5b\
    \xd7\x9d\xc6\x93\x35\x1c\x13\x3e\x49\x20\xd9\x2d\x48\x56\x47\x50\
    \x97\x2d\x83\xf2\x0a\x84\xa2\x20\xe7\x72\xb8\x87\x06\x09\xc4\xe3\
    \xf8\xba\x93\xe4\x52\x1a\x79\x70\xe0\xf9\x15\x45\x1c\x2d\x98\xc6\
    \xfb\x89\xdc\xde\x33\xf0\xcb\x21\xb0\x96\x36\xb0\xb8\x12\xd1\xb0\
    \x60\xdf\x4d\xf5\xc1\x2f\xde\xae\x99\xf8\xfa\x32\x78\x2d\xf0\xe2\
    \xc4\xdc\x1c\x10\x20\x00\x13\xd0\x2d\xd0\x00\x15\x1c\xb8\x16\x70\
    \x31\xb0\xb2\x88\xd7\xe3\xd9\xd4\xf0\x94\xf6\xed\xf3\x70\x60\x18\
    \x2c\xae\x91\xcc\x12\x1a\x82\x6c\x0e\x0e\x64\x92\x85\xd1\x73\x26\
    \x1b\x65\x7b\x16\x46\xc2\x1e\x3c\x19\x03\xcb\xa6\x59\x1f\x80\xaf\
    \x0a\x4d\x02\xa3\x4a\x21\x6e\x6f\xb6\x57\x3d\x92\xd5\x7a\x71\xf6\
    \xe5\xe1\x9c\x79\xff\x31\x78\x33\xfd\x69\x9e\x0d\x1b\x20\x1c\x86\
    \x47\xbc\x1e\x69\xf7\xaa\x5a\xdf\x9a\x35\x41\x45\xd4\x7a\xbd\x84\
    \x24\x09\xaf\x2c\xa3\x0a\xc1\xb8\x65\xd1\xa3\x16\xe8\xe8\x9b\xcc\
    \x8c\x8f\xab\x2f\xab\xf0\xeb\x4b\xd0\x36\x02\xe6\x7f\xed\xe1\xb4\
    \x12\x5c\x15\x70\x43\xa9\xa2\x6c\x0e\x87\x42\xf5\xa1\xd2\xd2\x48\
    \x71\x20\xe0\xc9\xe4\x72\x33\x13\xc9\xe4\x40\x72\x6a\xaa\x23\xae\
    \xaa\x6f\xa5\x61\x3a\xc9\xff\x89\xfe\x03\xa4\xb0\x04\x12\x96\xcf\
    \xa3\x95\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
    \x00\x00\x04\xc2\
    \x89\
    \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
    \x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\
    \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
    \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x03\x76\x00\x00\x03\x76\
    \x01\x7d\xd5\x82\xcc\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
    \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
    \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x04\x3f\x49\x44\
    \x41\x54\x78\xda\xed\x97\x6d\x4c\x1b\x75\x1c\xc7\xbf\xff\x6b\x29\
    \xed\x5d\x1f\xae\x6b\xef\xda\x52\x2c\x03\x56\x07\x13\x95\x21\x08\
    \x6c\x18\x86\x26\xb8\xb8\x2c\x71\x12\xb6\x45\x33\x63\x36\x49\xc4\
    \xf8\xb4\x09\x06\x4d\x8c\xee\xed\x24\x1a\x5f\x2c\x2e\x99\x22\x66\
    \xd3\x10\xc5\xe9\x44\x5e\x4c\x08\x42\xdc\x24\x61\xc6\xcc\x6c\x22\
    \x93\x6c\x53\x40\x5a\xe8\x0a\xf4\xe1\x4a\x1f\xef\xec\x95\x17\xe2\
    \xc3\x4a\x97\xb9\x99\x18\x3e\xc9\x37\x97\x5c\xee\x7f\xbf\xef\xfd\
    \x7e\xff\xcb\x7d\x8f\x48\x92\x84\xff\x12\x0a\xc0\xaa\x81\x55\x03\
    \xff\x5f\x03\xf4\x01\x7a\x77\x75\x47\x75\xac\xf1\xa3\xc6\x39\xf2\
    \x18\x31\xde\x52\x03\x74\x2b\xbd\x83\x71\x30\x1f\xee\x2d\xdd\xab\
    \x74\x9a\x9d\x46\xe5\x3a\xe5\x25\xf2\x1c\xd1\xdf\x12\x03\x86\x36\
    \xc3\x36\x36\x87\xed\x6e\x2b\x6f\xa3\xcc\x79\x66\x54\x6d\xac\x42\
    \xc3\x86\x06\x23\xd1\x93\xcb\xe4\x20\xa1\xb1\x0c\x25\xfe\x65\xd8\
    \x97\xd9\x7a\xbd\x4d\x7f\xb2\xe9\x8e\x26\xca\x92\x6b\x41\x30\x10\
    \x84\xcc\xf6\x8a\xed\x08\x45\x42\xa6\xbe\xb1\xbe\xf3\x00\x0a\x33\
    \x32\x40\x76\x12\x85\xf4\xb1\x94\x40\x86\x18\x5f\x31\xd6\x9a\xac\
    \xa6\xde\x5d\xce\x9d\x0a\xab\x2d\x59\x3c\x18\xc4\x72\x4a\x4c\x25\
    \x98\xe3\xe7\xac\x2b\x76\x80\x6e\xa3\x1d\xb9\x6b\x72\x47\x5b\xf7\
    \xb5\xd2\xa6\x16\xd3\x7e\x6f\xbb\xf7\x6d\xac\x00\xff\x02\x5f\x63\
    \xc9\xb3\xf4\x6f\x2b\x78\x50\xc9\xf3\x1c\x82\xa1\x00\x24\x11\x80\
    \x04\x48\x49\xcd\xb8\x67\x70\xe4\xcc\x11\x29\xb8\x10\x6c\x4f\xbb\
    \x07\xe8\x66\xda\xae\xd3\xe8\x46\xab\x1d\x15\x0c\x9b\xa5\x21\x39\
    \x0e\xdb\x5b\x96\x17\x2d\xcd\x48\x83\xad\xc5\x56\xc1\xaf\xe5\x07\
    \xea\x0b\xb6\x28\xcd\x66\x13\x84\x48\x00\xa1\xb0\x90\x92\xb0\x18\
    \x84\xc7\x35\x83\xce\xc1\x4e\x84\x3c\xa1\x76\xe9\x5d\xe9\x35\x2c\
    \xe3\x4f\x1f\x23\x5d\xb3\x8e\x57\x99\x55\x97\x1e\x28\xdc\xa4\x2d\
    \x76\x94\x40\x24\x09\x84\x17\x05\xf4\xff\x7c\x46\x72\x4d\xb8\x9b\
    \xdc\x6f\xba\xdf\xc3\x5f\xb0\xbf\x64\x2f\x33\x9b\xcd\xc3\x35\xeb\
    \x37\xaa\x38\xa3\x15\x89\x84\x28\x3f\x79\x52\x52\xea\x48\xc2\x12\
    \x8e\x0d\x74\xc1\xed\x9d\x3d\x1c\x3d\x1e\x7d\x26\xed\x5b\xa0\x36\
    \xa8\xbf\xaf\xc9\x2b\xd3\xe6\xf3\xf9\x10\x22\x7e\x44\x62\x02\x28\
    \x15\xc1\xfd\xce\x32\x62\xb7\xd9\x8e\xe6\xec\xcf\xd9\x83\x65\x58\
    \x0f\x58\xef\x34\x1b\xcc\xc3\x9b\x9c\x1b\x54\xac\xde\x80\xc5\x58\
    \x50\x5e\x93\x52\x38\x1a\x42\x56\x9c\xe0\x93\x81\x13\x98\xf1\x7a\
    \x3a\xfe\x28\x9e\xc6\x80\x99\x31\xe9\x78\x8d\x11\x81\xf0\x52\xf1\
    \xb0\x7c\xb3\xb8\x00\x51\x25\xe1\xbe\xe2\x62\xc2\xaf\xe1\x3a\xad\
    \xcf\x5a\x77\x21\x09\xf7\x34\xb7\x9e\xd3\x72\x67\xab\x8a\x0a\x55\
    \x5a\x96\x46\x28\xee\x97\xaf\x4d\x2a\x94\x5a\xc7\x2a\x68\x7c\xd6\
    \xd7\x83\x49\x8f\xab\x2b\x72\x3c\xb2\x0f\xcb\xb8\xe6\x08\xd4\x4f\
    \xa8\x9b\x39\xce\x78\x78\x4b\xc9\xdd\x44\xa3\xd3\x83\x52\x22\x29\
    \x02\xa2\x00\x08\x05\x50\x91\x18\x86\x46\xc6\xc5\x59\xef\xd5\xe7\
    \x59\x1d\xfb\xc6\xe6\xb2\x42\xb5\x8e\xd3\x22\x1e\x97\x20\xa6\xda\
    \x0e\x88\x71\x20\x4f\xed\xc0\xc9\xde\x3e\x9c\x9f\x1a\xff\x5c\xe8\
    \x16\x76\x20\x0d\x7f\x0b\x24\xf4\x1e\xba\x85\x37\x19\x0f\xd5\x96\
    \x17\x13\x8a\xc9\x96\x0d\x2c\x19\x91\x4d\x24\xa5\x58\x14\x71\xfa\
    \x9b\x49\x94\x97\x5a\xa1\xb1\xd2\x88\x8b\x62\x6a\xde\x62\x02\x60\
    \x14\x3a\x38\xb2\xf3\xd1\xd3\xf3\x35\xbe\xbb\x72\xe1\x54\xa0\x3b\
    \xb0\x15\x2b\xf0\x8f\x89\x88\x79\x94\x39\x68\x67\xb9\x57\x37\xd7\
    \xae\x25\x09\x9a\x82\x42\xee\x82\x72\xc9\x80\x92\x22\x30\x48\x6a\
    \x48\xea\x6c\xc4\x13\x22\x62\xb1\x18\x12\xd1\x28\xa2\xd1\x38\xa2\
    \x3e\x01\xa3\x83\x0b\x18\x9f\x73\x0d\xfa\x4f\xf8\xeb\x90\x01\xd7\
    \x8c\x64\xda\xdd\xda\x43\xb7\x69\xf9\xd6\x7b\xea\x4c\xf0\x65\xcf\
    \x83\xca\x42\xd2\x48\x52\x0a\x0a\x91\x84\x80\x45\x51\x80\x28\xef\
    \xf8\x04\x90\x88\x4b\x30\x44\x59\xcc\x0e\x31\xf8\x69\xd6\x35\xe2\
    \xfb\xc2\x57\x89\x0c\x49\x9b\x09\x75\x0f\xeb\xde\xc9\x33\xf0\x4f\
    \x15\xd5\x03\xf3\x5a\x0f\x14\xaa\xa5\x71\x10\x8a\x80\x00\x90\xd7\
    \xca\x33\xd7\x87\xf5\x98\xfe\x92\xc1\x8f\x2e\xf7\x0f\x0b\xbd\x0b\
    \xa5\xb8\x0e\x56\x0c\xa5\xda\x87\xb4\x1f\x14\x18\x2d\x8f\xdf\xd5\
    \x48\x30\x6f\xbc\x0a\x45\x6a\x14\x04\x20\x4b\x9b\x4e\x17\xd2\x63\
    \xaa\x8b\xc6\xb9\x5f\xa6\x2f\xfa\xfa\x7c\x45\xb8\x4e\x32\x4a\xc5\
    \x4c\x3d\xf3\xa9\xd3\x68\x7d\xe4\xde\x27\x95\xf0\x72\x1e\xb9\x0b\
    \x29\x0c\x82\x01\x57\x3a\xd4\x38\x7b\xf1\xb7\xcb\xfe\x41\x7f\xe1\
    \x4d\x0b\x24\xc2\x57\x42\xc3\xb8\xd7\x7d\xea\xc2\xfb\x22\xec\x11\
    \x1b\x54\x0c\x01\xa7\x60\x31\x71\x4c\x83\x91\xb1\xa9\x49\xb9\xf8\
    \x4d\x4f\x44\x42\xbf\xb0\x75\x7c\x6e\xf6\xf4\x58\x87\x88\xdb\x7d\
    \xeb\xf0\xeb\xd1\x6c\x7c\x7b\x6e\xc2\x1d\x18\x0a\x38\x70\x23\xc8\
    \x23\xc8\x5c\xaf\x53\xfa\x3a\x7a\xd8\x56\x69\x92\x34\x95\x9a\x69\
    \xf9\xdc\x8d\x6a\xf5\xcf\x68\xd5\xc0\xaa\x81\xdf\x01\x83\xc2\x0a\
    \xda\x8a\x75\x2e\x6f\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\
    \x82\
    \x00\x00\x04\xef\
    \x89\
    \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
    \x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\
    \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
    \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x03\x76\x00\x00\x03\x76\
    \x01\x7d\xd5\x82\xcc\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
    \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
    \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x04\x6c\x49\x44\
    \x41\x54\x78\xda\xc5\x56\xdf\x6f\x1b\x45\x10\x9e\xfb\x6d\xc7\x76\
    \x62\xb7\x76\x13\x0a\x11\x22\x05\xa9\xa2\xef\xbc\x20\xf1\x14\xde\
    \xf8\x37\x80\x67\xd4\x97\x0a\x89\xf2\x04\xad\x54\xf8\x7f\x90\x8a\
    \x04\xaa\x2a\xb5\x6f\x45\x34\x84\x22\x88\xd4\x22\x4c\x8c\x93\xda\
    \x71\xb1\xef\xec\xbb\xdb\x5b\x66\x46\x1e\xfb\x7a\x77\xe6\x42\xa4\
    \xd2\x4d\xd6\x33\xbb\x77\x37\xf3\xed\x37\xdf\xee\x9d\xad\xb5\x86\
    \x97\xd9\xec\xb9\x35\xb0\xeb\x97\x09\x40\x5f\xfd\xe4\xea\xed\x44\
    \xab\x5d\xb9\xf0\xce\xbb\xef\xc1\xce\x9b\x97\xc1\xb1\x2c\x80\x0c\
    \x4b\x86\x81\x78\xb1\x9b\xa6\x01\xfc\x67\x48\xe7\x8b\x6c\xf1\x12\
    \xcf\xc5\x51\x0c\x7b\x3f\xfd\x08\xf7\xef\xde\x01\x69\xad\xd6\xb9\
    \x6f\xaf\x7f\x7e\xfd\xfd\x34\x00\xf0\xfd\xc9\xee\xc3\xbd\x87\xb0\
    \xb9\xb9\x05\x17\x3a\x1d\x38\xdf\xb9\x00\xdb\xdb\xaf\x83\xe7\x3a\
    \x85\xc8\x8d\xf9\x0f\x59\x4e\x9e\xf5\x19\x84\x01\xfe\x2c\x84\x7e\
    \xff\x2f\x98\x06\x53\x90\x36\x48\x9e\xee\x66\x19\x80\x24\x49\xa0\
    \x56\xab\x81\x63\xdb\xec\x9b\xa0\x79\x85\xbf\xf7\x4f\x8a\x92\x67\
    \x18\x79\x7e\x60\xa4\x00\xbe\xd6\x6e\xa0\xd1\x1c\x33\x9d\xab\x10\
    \x40\x73\xa3\x09\x9e\xe7\x2d\x6e\xb0\x2c\x03\xde\x7a\xb5\xc5\xbe\
    \xce\x26\xe7\x44\xe2\x8a\xe5\x39\x61\x80\xc7\xc1\x34\xa4\x4b\xe5\
    \x00\x68\x37\x34\x1a\x0d\xf1\xf9\x61\x0b\x19\x70\x6d\x0b\x56\xd5\
    \x60\x35\x00\x89\x89\xc9\xa8\x43\xc2\x31\x53\xb9\x0a\x19\xc8\xc4\
    \xd7\x08\x00\x6f\xb0\x25\x9a\x5c\x28\x2e\x83\x9e\x07\xa6\x84\x1a\
    \x7f\xd0\xe5\x71\xac\x14\x8f\xd3\xf1\x95\x52\xe5\x00\x12\x8d\xb8\
    \xf1\xc1\x9f\x1f\x1f\x43\x51\xd3\x99\x91\xd6\xe2\x31\x12\xb2\x3c\
    \xd8\x3a\x57\x03\x95\xe4\x4a\x50\x0e\x80\x86\x11\x3e\xb9\xbd\xb9\
    \xfe\xbc\xc8\x34\xff\xe7\xe8\x24\xa3\x19\xc8\x82\x0d\xee\xb3\x50\
    \xd1\x8a\x4f\xa7\x81\x74\x53\x71\x02\x31\x75\x2b\x0d\xac\x00\x88\
    \xac\x5e\x92\xb2\x5d\xfa\x31\x2e\x42\x69\xfd\xdf\x35\x10\x27\x0a\
    \x22\xda\x8e\x91\x5a\x5d\x02\x59\xbd\xb8\x39\x06\x80\x35\xa0\x12\
    \x7d\x86\x12\xe0\xd3\x51\xac\xa0\x77\x3c\x96\xa9\x32\x2d\x48\xed\
    \xc5\xe5\xd6\x5a\xf7\x90\x4d\x55\x2e\x42\x9d\x01\xa0\x70\x4c\xf4\
    \x35\x1b\x15\xd9\x57\xf9\x44\x99\x64\x3c\xbb\x60\x80\x1d\x8a\xc1\
    \xc9\xd3\xf1\x75\x21\x03\x39\x0d\x68\x16\xa1\x51\xb2\x74\x9d\x06\
    \x25\xe3\xa5\x06\xc8\x61\x0d\xa4\xe3\x27\xa7\xd1\xc0\xbd\xbb\xdf\
    \x43\xaf\xd7\x83\xc0\xf7\x4b\xe8\x2f\x16\xa6\x30\x53\x5d\x5b\x83\
    \x27\xbf\x3d\xca\x6a\xa0\xbc\x04\x4f\x7e\xfd\x85\x7a\xfa\x0d\x98\
    \xb5\xc5\xa0\x56\x2b\xbe\xa4\x04\x38\xf9\x82\x5b\xc9\x49\xf8\xff\
    \x7d\x19\x15\x6b\x40\xbf\x58\x06\xce\x5e\x82\x0f\x3f\xfe\x08\xde\
    \xd8\xd9\x29\xaa\xe5\xca\x3a\x0f\x06\x03\xf8\xf4\xda\x35\x30\x4d\
    \x13\xe3\x6a\xde\xd2\x9b\x9d\xce\xbf\x8b\x30\x8e\xe3\x5c\x60\xaf\
    \xe2\xb1\x8a\xc7\xe3\xb1\x08\x2f\x2b\x30\x9a\x93\x2e\x73\xfc\x61\
    \x53\xa9\x54\xc0\xb2\x2c\xde\x09\xb3\xd9\x2c\x1f\x5f\x00\x14\x09\
    \x43\x5a\xfa\x04\xbb\xf1\xc5\x97\x74\xac\xca\xe7\x1b\xdc\xb8\x79\
    \x93\x56\x28\xa0\xe8\x3e\xb2\xcb\x17\x0f\x83\x32\x89\x6f\xfa\xb2\
    \xa2\xf9\x33\x00\x98\x07\xa5\xf6\xf4\xf8\x18\xa4\x0d\x4f\x4e\x28\
    \x09\x01\xa0\x95\x91\xcf\xcf\x4b\xd7\xcb\x17\x82\x98\x53\x00\x28\
    \xa0\x28\x9c\x4e\x29\x78\x16\x20\x51\x4b\xf3\x44\x2d\xdb\x28\x8a\
    \x64\xe5\xc2\x02\x67\x15\x30\x9a\x80\x21\x23\xd5\x6a\x15\x6a\xf5\
    \x3a\x0c\x07\x83\x02\x0d\x14\x20\xb4\x6c\xbb\x10\x40\x14\x47\x10\
    \x04\x81\x24\x20\x2b\x4c\x90\xe5\x1e\x22\x28\x7b\x0e\x34\xa6\x18\
    \x74\xaf\x01\x10\xe0\xa2\xa2\x38\x3e\x1d\x03\xe0\xba\x1c\x7c\x8a\
    \x0f\xc5\x71\x24\xe5\x20\x2b\xc2\xa2\xd5\x4b\x09\x24\x39\x5f\x8f\
    \x42\x64\x87\x45\xa8\xf9\x85\x64\xa0\xb5\x4d\x0b\x9e\x8d\x46\xd0\
    \x58\x5f\x2f\x02\xa0\x7a\x60\xc0\x56\xf6\xc4\x0a\xfc\x00\x1c\xc7\
    \x81\x31\x5a\x7f\x32\x61\x5d\x34\x9b\x4d\x14\xa2\x2f\x49\x85\xfe\
    \x85\x0d\xc3\x10\x81\xd1\x7c\xb2\x10\xa8\x39\xdf\x9e\xed\x76\x1b\
    \xfa\xfd\x3e\x18\xd8\x34\x36\x02\xc0\x83\xcb\x97\x2e\x7d\x50\xa9\
    \xd6\xbe\xc1\x52\xb5\xd3\xfb\x55\xe8\xbe\xf5\xd5\x2d\x01\x45\x9d\
    \xb6\x26\x05\xcf\x89\x50\xfc\x28\x0c\x59\x2b\x02\xc0\x32\x4d\x9e\
    \xef\x76\xbb\x2c\x5e\x6c\x55\x4c\x1b\x08\x03\xc6\xa3\x83\x83\x3f\
    \x37\x36\x36\xbe\xde\x7e\xe5\xe2\x67\x78\xc3\x1a\x8b\x30\x0a\x39\
    \x91\x3d\xd7\x42\x6a\xc5\x44\xbd\xd0\x9f\x3e\x8c\x28\xb8\x68\x2a\
    \x5d\x32\xd1\x90\x8c\x43\x34\xe7\xb1\x77\x05\x00\xdd\xf9\xf7\x68\
    \x34\xfa\x0e\x95\xdf\xf2\x3c\xef\x22\x11\xf0\xf6\x95\x2b\xd6\x0f\
    \x0f\x1e\xec\xb8\xae\xeb\xb9\x8e\xe3\xa2\x28\x5d\x04\xe3\x61\x12\
    \x87\xca\x27\x87\x80\xe6\x2f\x38\x15\x22\x98\x30\xc2\x36\x1c\x0e\
    \x7d\xdb\xb1\x0f\x0e\x0f\x0f\x67\x78\xd5\x58\xe6\x60\xba\x15\x3a\
    \xf7\xd0\x9b\x61\xe7\x12\x70\x0c\x8c\xe5\xa3\xbb\x17\xcc\x66\x8f\
    \xb1\x3b\x34\xfd\x47\xb7\x9b\x54\xaa\xd5\x10\x4f\xb5\x44\xd0\x23\
    \x4b\x80\x40\x89\x15\x8d\xc0\x34\xd5\xb5\x5e\xaf\x1b\x78\xfa\x19\
    \x84\xe7\x04\xcf\x88\xfd\xfd\xfd\xe4\xe8\xe8\xc8\x9d\x4c\x26\x14\
    \xc7\xcc\x7f\x35\x70\xf2\x67\x94\xf7\x1f\xdf\x9a\xd2\x93\xfb\x43\
    \xb7\xa7\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
    "
    
    qt_resource_name = "\
    \x00\x05\
    \x00\x6f\xa6\x53\
    \x00\x69\
    \x00\x63\x00\x6f\x00\x6e\x00\x73\
    \x00\x0e\
    \x0e\xa1\x23\xc7\
    \x00\x68\
    \x00\x65\x00\x6c\x00\x70\x00\x2d\x00\x61\x00\x62\x00\x6f\x00\x75\x00\x74\x00\x2e\x00\x70\x00\x6e\x00\x67\
    \x00\x0b\
    \x06\x3d\x10\x27\
    \x00\x67\
    \x00\x6f\x00\x2d\x00\x64\x00\x6f\x00\x77\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\
    \x00\x11\
    \x03\x45\xee\x87\
    \x00\x68\
    \x00\x65\x00\x6c\x00\x70\x00\x2d\x00\x63\x00\x6f\x00\x6e\x00\x74\x00\x65\x00\x6e\x00\x74\x00\x73\x00\x2e\x00\x70\x00\x6e\x00\x67\
    \
    \x00\x0f\
    \x0f\x88\xbf\x47\
    \x00\x63\
    \x00\x6f\x00\x6e\x00\x74\x00\x61\x00\x63\x00\x74\x00\x2d\x00\x6e\x00\x65\x00\x77\x00\x2e\x00\x70\x00\x6e\x00\x67\
    \x00\x0b\
    \x08\xcf\xe2\x07\
    \x00\x69\
    \x00\x6e\x00\x73\x00\x74\x00\x61\x00\x6c\x00\x6c\x00\x2e\x00\x70\x00\x6e\x00\x67\
    \x00\x14\
    \x07\x40\xa2\xc7\
    \x00\x61\
    \x00\x70\x00\x70\x00\x6c\x00\x69\x00\x63\x00\x61\x00\x74\x00\x69\x00\x6f\x00\x6e\x00\x2d\x00\x65\x00\x78\x00\x69\x00\x74\x00\x2e\
    \x00\x70\x00\x6e\x00\x67\
    \x00\x11\
    \x03\x53\x87\x07\
    \x00\x64\
    \x00\x69\x00\x61\x00\x6c\x00\x6f\x00\x67\x00\x2d\x00\x63\x00\x61\x00\x6e\x00\x63\x00\x65\x00\x6c\x00\x2e\x00\x70\x00\x6e\x00\x67\
    \
    \x00\x13\
    \x04\x68\xf2\x47\
    \x00\x64\
    \x00\x69\x00\x61\x00\x6c\x00\x6f\x00\x67\x00\x2d\x00\x6f\x00\x6b\x00\x2d\x00\x61\x00\x70\x00\x70\x00\x6c\x00\x79\x00\x2e\x00\x70\
    \x00\x6e\x00\x67\
    \x00\x11\
    \x0f\xe3\xd5\x67\
    \x00\x64\
    \x00\x6f\x00\x63\x00\x75\x00\x6d\x00\x65\x00\x6e\x00\x74\x00\x2d\x00\x73\x00\x61\x00\x76\x00\x65\x00\x2e\x00\x70\x00\x6e\x00\x67\
    \
    "
    
    qt_resource_struct = "\
    \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
    \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
    \x00\x00\x00\x00\x00\x02\x00\x00\x00\x09\x00\x00\x00\x03\
    \x00\x00\x00\x4e\x00\x00\x00\x00\x00\x01\x00\x00\x0b\x78\
    \x00\x00\x00\xe4\x00\x00\x00\x00\x00\x01\x00\x00\x27\xd9\
    \x00\x00\x01\x0c\x00\x00\x00\x00\x00\x01\x00\x00\x30\x7c\
    \x00\x00\x00\x32\x00\x00\x00\x00\x00\x01\x00\x00\x05\xd7\
    \x00\x00\x00\xb6\x00\x00\x00\x00\x00\x01\x00\x00\x20\xf5\
    \x00\x00\x00\x9a\x00\x00\x00\x00\x00\x01\x00\x00\x19\x79\
    \x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
    \x00\x00\x00\x76\x00\x00\x00\x00\x00\x01\x00\x00\x11\xbb\
    \x00\x00\x01\x38\x00\x00\x00\x00\x00\x01\x00\x00\x35\x42\
    "
    
    def qInitResources():
        QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
    
    def qCleanupResources():
        QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
    
    qInitResources()
    apt-offline-1.3.1/apt_offline_gui/AptOfflineQtCreateProfile.py0000644000000000000000000001161712066662146021332 0ustar  # -*- coding: utf-8 -*-
    import sys,os
    from PyQt4 import QtCore, QtGui
    
    from apt_offline_gui.Ui_AptOfflineQtCreateProfile import Ui_CreateProfile
    from apt_offline_gui.UiDataStructs import SetterArgs
    from apt_offline_gui import AptOfflineQtCommon as guicommon
    import apt_offline_core.AptOfflineCoreLib
    
    
    class AptOfflineQtCreateProfile(QtGui.QDialog):
        def __init__(self, parent=None):
            QtGui.QWidget.__init__(self, parent)
            self.ui = Ui_CreateProfile()
            self.ui.setupUi(self)
            
            # Connect the clicked signal of the Browse button to it's slot
            QtCore.QObject.connect(self.ui.browseFilePathButton, QtCore.SIGNAL("clicked()"),
                            self.popupDirectoryDialog )
                            
            # Connect the clicked signal of the Save to it's Slot - accept
            QtCore.QObject.connect(self.ui.createProfileButton, QtCore.SIGNAL("clicked()"),
                            self.CreateProfile )
                            
            # Connect the clicked signal of the Cancel to it's Slot - reject
            QtCore.QObject.connect(self.ui.cancelButton, QtCore.SIGNAL("clicked()"),
                            self.reject )
            
            # Disable or Enable the Package List field
            QtCore.QObject.connect(self.ui.installPackagesCheckBox, QtCore.SIGNAL("toggled(bool)"),
                            self.PackageListFieldStatus )
            
        def PackageListFieldStatus(self):
            # If Install Packages Box is selected
            self.isFieldChecked = self.ui.installPackagesCheckBox.isChecked()
            self.ui.packageList.setEnabled(self.isFieldChecked)
        
        def CreateProfile(self):
            # Is the Update requested
            self.updateChecked = self.ui.updateCheckBox.isChecked()
            # Is Upgrade requested
            self.upgradeChecked = self.ui.upgradePackagesCheckBox.isChecked()
            # Is Install Requested
            self.installChecked = self.ui.installPackagesCheckBox.isChecked()
    
            # Clear the consoleOutputHolder
            self.ui.consoleOutputHolder.setText("")
            
            self.filepath = str(self.ui.profileFilePath.text())
            
            if os.path.exists(os.path.dirname(self.filepath)) == False:
                if (len(self.filepath) == 0):
                    self.ui.consoleOutputHolder.setText ( \
                        guicommon.style("Please select a file to store the signature!",'red'))
                else:
                    self.ui.consoleOutputHolder.setText ( \
                        guicommon.style("Could not access  %s" % self.filepath,'red'))
                return
            
            # If atleast one is requested
            if self.updateChecked or self.upgradeChecked or self.installChecked:
                if self.installChecked:
                    self.packageList = str(self.ui.packageList.text()).split(",")
                else:
                    self.packageList = None
    
                # setup i/o redirects before call
                sys.stdout = self
                sys.stderr = self
                 
                args = SetterArgs(filename=self.filepath, update=self.updateChecked, upgrade=self.upgradeChecked, install_packages=self.packageList, simulate=False)
                returnStatus = apt_offline_core.AptOfflineCoreLib.setter(args)
    
                if(returnStatus != False):  # right now it returns None, I think it doesn't return at all but sys.exits on failure
                    # TODO ^ fixup this behaviour
                    guicommon.updateInto(self.ui.consoleOutputHolder, guicommon.style("Completed.","green_fin"))
                    self.ui.createProfileButton.setEnabled(False)
                    self.ui.cancelButton.setText("Finish")
                    self.ui.cancelButton.setIcon(QtGui.QIcon())
            else:
                pass
            
        
        def popupDirectoryDialog(self):
            # Popup a Directory selection box
            signatureFilePath = os.path.join (os.path.expanduser("~"), "/Desktop/"+"apt-offline.sig")
            directory = QtGui.QFileDialog.getSaveFileName(self, u'Select a filename to save the signature', signatureFilePath, "apt-offline Signatures (*.sig)")
            # Show the selected file path in the field marked for showing directory path
            self.ui.profileFilePath.setText(directory)
    
        def write(self, text):
            # redirects console output to our consoleOutputHolder
            text=text.strip()
            if (len(text)>2):
                guicommon.updateInto(self.ui.consoleOutputHolder,text)
    
        def flush(self):
            ''' nothing to do :D '''
    
        def resetUI(self):
            self.ui.updateCheckBox.setChecked(False)
            self.ui.upgradePackagesCheckBox.setChecked(False)
            self.ui.installPackagesCheckBox.setChecked(False)
            self.ui.cancelButton.setText("Close")
            self.ui.createProfileButton.setEnabled(True)
            self.ui.consoleOutputHolder.setText("")
            self.ui.profileFilePath.setText("")
            self.ui.packageList.setText("")
    
    
    if __name__ == "__main__":
        app = QtGui.QApplication(sys.argv)
        myapp = AptOfflineQtCreateProfile()
        myapp.show()
        sys.exit(app.exec_())
    apt-offline-1.3.1/apt_offline_gui/AptOfflineQtInstall.py0000644000000000000000000001646612125763750020222 0ustar  # -*- coding: utf-8 -*-
    import os,sys, thread
    from PyQt4 import QtCore, QtGui
    
    from apt_offline_gui.Ui_AptOfflineQtInstall import Ui_AptOfflineQtInstall
    from apt_offline_gui.UiDataStructs import InstallerArgs
    from apt_offline_gui import AptOfflineQtCommon as guicommon
    import apt_offline_core.AptOfflineCoreLib
    
    from apt_offline_gui.AptOfflineQtInstallBugList import AptOfflineQtInstallBugList
    
    class Worker(QtCore.QThread):
        def __init__(self, parent = None):
            QtCore.QThread.__init__(self, parent)
            self.parent = parent
            self.exiting = False
    
        def __del__(self):
            self.exiting = True
            self.wait()
    
        def run(self):
            # setup i/o redirects before call
            sys.stdout = self
            sys.stderr = self
            apt_offline_core.AptOfflineCoreLib.installer(self.args)
    
        def setArgs (self,args):
            self.args = args
    
        def write(self, text):
            # redirects console output to our consoleOutputHolder
            # extract chinese whisper from text
            if ('.deb' in text and 'synced' in text):
                try:
                    text = guicommon.style("Package : ",'orange') + guicommon.style(text.split("/")[-1],'green')
                except:
                    pass
                self.emit (QtCore.SIGNAL('output(QString)'), text)
            elif ('apt/lists' in text):
                try:
                    # this part is always done on a linux system so we can hardcode / for a while
                    text = guicommon.style("Update : ",'orange') + guicommon.style(text.split("/")[-1],'green')
                except:
                    # let the text be original otherwise
                    pass
                self.emit (QtCore.SIGNAL('output(QString)'), text)
            elif ('[' in text and ']' in text):
                try:
                    progress = str(apt_offline_core.AptOfflineCoreLib.totalSize[0])
                    total = str(apt_offline_core.AptOfflineCoreLib.totalSize[1])
                    self.emit (QtCore.SIGNAL('progress(QString,QString)'), progress,total)
                except:
                    ''' nothing to do '''
            else:
                self.emit (QtCore.SIGNAL('output(QString)'), guicommon.style(text,'red'))
                                
        def flush(self):
            ''' nothing to do :D '''
            
        def quit(self):
            self.emit (QtCore.SIGNAL('finished()'))
            
            
    class AptOfflineQtInstall(QtGui.QDialog):
        def __init__(self, parent=None):
            QtGui.QWidget.__init__(self, parent)
            self.ui = Ui_AptOfflineQtInstall()
            self.ui.setupUi(self)
            
            # Connect the clicked signal of the Browse button to it's slot
            QtCore.QObject.connect(self.ui.browseFilePathButton, QtCore.SIGNAL("clicked()"),
                            self.popupDirectoryDialog )
                            
            # Connect the clicked signal of the Save to it's Slot - accept
            QtCore.QObject.connect(self.ui.startInstallButton, QtCore.SIGNAL("clicked()"),
                            self.StartInstall )
                            
            # Connect the clicked signal of the Cancel to it's Slot - reject
            QtCore.QObject.connect(self.ui.cancelButton, QtCore.SIGNAL("clicked()"),
                            self.reject )
            
            QtCore.QObject.connect(self.ui.bugReportsButton, QtCore.SIGNAL("clicked()"),
                            self.showBugReports )
            
            QtCore.QObject.connect(self.ui.zipFilePath, QtCore.SIGNAL("editingFinished()"),
                            self.ControlStartInstallBox )
    
            QtCore.QObject.connect(self.ui.zipFilePath, QtCore.SIGNAL("textChanged(QString)"),
                            self.ControlStartInstallBox )
            
            self.worker = Worker(parent=self)
            QtCore.QObject.connect(self.worker, QtCore.SIGNAL("output(QString)"),
                            self.updateLog )
            QtCore.QObject.connect(self.worker, QtCore.SIGNAL("progress(QString,QString)"),
                            self.updateProgress )
            QtCore.QObject.connect(self.worker, QtCore.SIGNAL("status(QString)"),
                            self.updateStatus )
            QtCore.QObject.connect(self.worker, QtCore.SIGNAL("finished()"),
                            self.finishedWork )
            QtCore.QObject.connect(self.worker, QtCore.SIGNAL("terminated()"),
                            self.finishedWork )
            
        def StartInstall(self):
            # gui validation
            # Clear the consoleOutputHolder
            self.ui.rawLogHolder.setText("")
            self.filepath = str(self.ui.zipFilePath.text())
    
            # parse args
            args = InstallerArgs(filename=self.filepath, progress_bar=self.ui.statusProgressBar, progress_label=self.ui.progressStatusDescription )
    
            self.disableActions()
            self.ui.progressStatusDescription.setText("Syncing updates")
            self.worker.setArgs (args)
            self.worker.start()
    
        def showBugReports(self):
    
            self.filepath = str(self.ui.zipFilePath.text())
    
            self.bugReportsDialog = AptOfflineQtInstallBugList(self.filepath)
            self.bugReportsDialog.filepath= self.filepath
            self.bugReportsDialog.show()
            
    
        def popupDirectoryDialog(self):
    
            # Popup a Directory selection box
            if self.ui.browseFileFoldercheckBox.isChecked() is True:
                    directory  = QtGui.QFileDialog.getExistingDirectory(self, u'Select the folder')
            else:
                    directory = QtGui.QFileDialog.getOpenFileName(self, u'Select the Zip File')
            
            # Show the selected file path in the field marked for showing directory path
            self.ui.zipFilePath.setText(directory)
            self.ui.zipFilePath.setFocus()
        
        def ControlStartInstallBox(self):
            if self.ui.zipFilePath.text().isEmpty():
                self.ui.startInstallButton.setEnabled(False)
    	    # We do the same for bug reports button
                self.ui.bugReportsButton.setEnabled(False)
            else:
                self.ui.startInstallButton.setEnabled(True)
                self.ui.bugReportsButton.setEnabled(True)
                
        def updateLog(self,text):
            guicommon.updateInto (self.ui.rawLogHolder,text)
    
        def updateStatus(self,text):
            # status handler
            self.ui.progressStatusDescription.setText(text)
    
        def updateProgress(self,progress,total):
            try:
                # try parsing numbers and updating progressBar
                percent = (float(progress)/float(total))*100
                self.ui.statusProgressBar.setValue (percent)
            except:
                 ''' nothing to do '''
    
        def finishedWork(self):
            self.enableActions()
            guicommon.updateInto (self.ui.rawLogHolder,
                guicommon.style("Finished syncting updates/packages","green_fin"))
            self.ui.progressStatusDescription.setText("Finished Syncing")
            
        def disableActions(self):
            self.ui.cancelButton.setEnabled(False)
            self.ui.startInstallButton.setEnabled(False)
            self.ui.bugReportsButton.setEnabled(False)
            self.ui.browseFilePathButton.setEnabled(False)
            self.ui.zipFilePath.setEnabled(False)
    
        def enableActions(self):
            self.ui.cancelButton.setEnabled(True)
            self.ui.startInstallButton.setEnabled(True)
            self.ui.bugReportsButton.setEnabled(True)
            self.ui.browseFilePathButton.setEnabled(True)
            self.ui.zipFilePath.setEnabled(True)
    
    
    if __name__ == "__main__":
            app = QtGui.QApplication(sys.argv)
            myapp = AptOfflineQtInstall()
            myapp.show()
            sys.exit(app.exec_())
    apt-offline-1.3.1/apt_offline_gui/AptOfflineQtMain.py0000644000000000000000000001111212125763750017457 0ustar  import sys, os
    from PyQt4 import QtCore, QtGui
    
    from apt_offline_gui.Ui_AptOfflineQtMain import Ui_AptOfflineMain
    
    from apt_offline_gui.AptOfflineQtCreateProfile import AptOfflineQtCreateProfile
    from apt_offline_gui.AptOfflineQtFetch import AptOfflineQtFetch
    from apt_offline_gui.AptOfflineQtInstall import AptOfflineQtInstall
    from apt_offline_gui.AptOfflineQtAbout import AptOfflineQtAbout
    
    class AptOfflineQtMain(QtGui.QMainWindow):
        def __init__(self, parent=None):
            QtGui.QWidget.__init__(self, parent)
            self.ui = Ui_AptOfflineMain()
            self.ui.setupUi(self)
            
            # Configure the various actions
            self.ConfigureCreateProfile()
            self.ConfigureDownload()
            self.ConfigureInstall()
            self.ConfigureAbout()
            self.ConfigureMenuExit()
            
            # Configure Hover over Buttons for Help
            self.CreateButtonHoverHelp()
            
        def ConfigureCreateProfile(self):
            QtCore.QObject.connect(self.ui.menuCreateProfile, QtCore.SIGNAL("triggered()"), self.CreateProfile)
            QtCore.QObject.connect(self.ui.createProfileButton,QtCore.SIGNAL("clicked()"), self.CreateProfile)
            # Create an object and do not show it
            self.createProfileDialog = AptOfflineQtCreateProfile()
            # setup hover hack
            self.ui.createProfileButton.installEventFilter(self)
            
        def ConfigureDownload(self):
            QtCore.QObject.connect(self.ui.menuDownload, QtCore.SIGNAL("triggered()"), self.DownloadPackagesUpgrades)
            QtCore.QObject.connect(self.ui.downloadButton, QtCore.SIGNAL("clicked()"), self.DownloadPackagesUpgrades)
            # Create an object for download dialog
            self.createDownloadDialog = AptOfflineQtFetch()
            # setup hover hack
            self.ui.downloadButton.installEventFilter(self)
        
        def ConfigureInstall(self):
            QtCore.QObject.connect(self.ui.menuInstall, QtCore.SIGNAL("triggered()"), self.InstallPackagesUpgrades)
            QtCore.QObject.connect(self.ui.restoreButton, QtCore.SIGNAL("clicked()"), self.InstallPackagesUpgrades)
            # Create an object for Install dialog
            self.createInstallDialog = AptOfflineQtInstall()
            # setup hover hack
            self.ui.restoreButton.installEventFilter(self)
    
        def ConfigureAbout(self):
            QtCore.QObject.connect(self.ui.menuAbout, QtCore.SIGNAL("triggered()"), self.ShowAbout)
            # Create an object for About Dialog
            self.createAboutDialog = AptOfflineQtAbout()
            
        def ConfigureMenuExit(self):
            QtCore.QObject.connect(self.ui.menuExit, QtCore.SIGNAL("triggered()"), self.ExitApp)
            QtCore.QObject.connect(self.ui.exitButton, QtCore.SIGNAL("clicked()"), self.ExitApp)
    
        def eventFilter(self,target,event):
            # hover hack for 3 buttons
            if event.type() == QtCore.QEvent.HoverEnter:
                if target.objectName() == 'createProfileButton':
                    self.ui.descriptionField.setText("Click here to generate a signature of this machine.")
                if target.objectName() == 'downloadButton':
                    self.ui.descriptionField.setText("Once you are on a internet connected machine, use this to download packages as per your signature file.")
                if target.objectName() == 'restoreButton':
                    self.ui.descriptionField.setText("Once you've downloaded all the packages, click here to install them on the offline machine.")
                    
            if event.type() == QtCore.QEvent.HoverLeave:
                self.ui.descriptionField.setText("Hover your mouse over the buttons to get the description.")
            return False
            
        
        def CreateProfile(self):
            if os.geteuid() != 0:
                    QtGui.QMessageBox.critical(self, "Error", "You need to run with root priviliges")
                    return
            # Code for creating Modal Dialog for Create Profile
            self.createProfileDialog.resetUI()
            self.createProfileDialog.show()
    
        def DownloadPackagesUpgrades(self):
            # Code for creating Modal Dialog for Downloading Packages/Upgrades
            self.createDownloadDialog.resetUI()
            self.createDownloadDialog.show()
    
        def InstallPackagesUpgrades(self):
            #if os.geteuid() != 0:
            #        QtGui.QMessageBox.critical(self, "Error", "You need to run with root priviliges")
            #        return
            # Code for creating Modal Dialog for Installing Packages/Upgrades
            self.createInstallDialog.show()
        
        def ShowAbout(self):
            # Code for showing Model Dialog for About Application
            self.createAboutDialog.show()
    
        def CreateButtonHoverHelp(self):
            pass
    
        def ExitApp(self):
            self.close()
    
    apt-offline-1.3.1/apt_offline_gui/Ui_AptOfflineQtInstall.py0000644000000000000000000001354612125763750020653 0ustar  # -*- coding: utf-8 -*-
    
    # Form implementation generated from reading ui file 'AptOfflineQtInstall.ui'
    #
    # Created: Mon Dec 31 16:02:36 2012
    #      by: PyQt4 UI code generator 4.9.3
    #
    # WARNING! All changes made in this file will be lost!
    
    from PyQt4 import QtCore, QtGui
    
    try:
        _fromUtf8 = QtCore.QString.fromUtf8
    except AttributeError:
        _fromUtf8 = lambda s: s
    
    class Ui_AptOfflineQtInstall(object):
        def setupUi(self, AptOfflineQtInstall):
            AptOfflineQtInstall.setObjectName(_fromUtf8("AptOfflineQtInstall"))
            AptOfflineQtInstall.setWindowModality(QtCore.Qt.ApplicationModal)
            AptOfflineQtInstall.resize(466, 400)
            sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred)
            sizePolicy.setHorizontalStretch(0)
            sizePolicy.setVerticalStretch(0)
            sizePolicy.setHeightForWidth(AptOfflineQtInstall.sizePolicy().hasHeightForWidth())
            AptOfflineQtInstall.setSizePolicy(sizePolicy)
            AptOfflineQtInstall.setMinimumSize(QtCore.QSize(466, 400))
            AptOfflineQtInstall.setMaximumSize(QtCore.QSize(466, 400))
            self.zipFilePath = QtGui.QLineEdit(AptOfflineQtInstall)
            self.zipFilePath.setGeometry(QtCore.QRect(30, 32, 270, 30))
            self.zipFilePath.setObjectName(_fromUtf8("zipFilePath"))
            self.browseFilePathButton = QtGui.QPushButton(AptOfflineQtInstall)
            self.browseFilePathButton.setGeometry(QtCore.QRect(320, 32, 110, 30))
            self.browseFilePathButton.setObjectName(_fromUtf8("browseFilePathButton"))
            self.startInstallButton = QtGui.QPushButton(AptOfflineQtInstall)
            self.startInstallButton.setEnabled(False)
            self.startInstallButton.setGeometry(QtCore.QRect(31, 75, 130, 30))
            icon = QtGui.QIcon()
            icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/icons/icons/dialog-ok-apply.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
            self.startInstallButton.setIcon(icon)
            self.startInstallButton.setObjectName(_fromUtf8("startInstallButton"))
            self.cancelButton = QtGui.QPushButton(AptOfflineQtInstall)
            self.cancelButton.setGeometry(QtCore.QRect(320, 75, 110, 30))
            icon1 = QtGui.QIcon()
            icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/icons/icons/application-exit.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
            self.cancelButton.setIcon(icon1)
            self.cancelButton.setObjectName(_fromUtf8("cancelButton"))
            self.statusProgressBar = QtGui.QProgressBar(AptOfflineQtInstall)
            self.statusProgressBar.setGeometry(QtCore.QRect(30, 146, 410, 20))
            self.statusProgressBar.setProperty("value", 0)
            self.statusProgressBar.setObjectName(_fromUtf8("statusProgressBar"))
            self.label = QtGui.QLabel(AptOfflineQtInstall)
            self.label.setGeometry(QtCore.QRect(30, 2, 200, 30))
            font = QtGui.QFont()
            font.setPointSize(10)
            self.label.setFont(font)
            self.label.setObjectName(_fromUtf8("label"))
            self.label_2 = QtGui.QLabel(AptOfflineQtInstall)
            self.label_2.setGeometry(QtCore.QRect(34, 120, 70, 16))
            font = QtGui.QFont()
            font.setPointSize(10)
            self.label_2.setFont(font)
            self.label_2.setObjectName(_fromUtf8("label_2"))
            self.progressStatusDescription = QtGui.QLabel(AptOfflineQtInstall)
            self.progressStatusDescription.setGeometry(QtCore.QRect(90, 120, 53, 15))
            font = QtGui.QFont()
            font.setPointSize(10)
            self.progressStatusDescription.setFont(font)
            self.progressStatusDescription.setObjectName(_fromUtf8("progressStatusDescription"))
            self.rawLogHolder = QtGui.QTextEdit(AptOfflineQtInstall)
            self.rawLogHolder.setGeometry(QtCore.QRect(30, 180, 411, 191))
            self.rawLogHolder.setObjectName(_fromUtf8("rawLogHolder"))
            self.bugReportsButton = QtGui.QPushButton(AptOfflineQtInstall)
            self.bugReportsButton.setEnabled(False)
            self.bugReportsButton.setGeometry(QtCore.QRect(172, 75, 130, 30))
            icon2 = QtGui.QIcon()
            icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/icons/icons/help-about.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
            self.bugReportsButton.setIcon(icon2)
            self.bugReportsButton.setObjectName(_fromUtf8("bugReportsButton"))
            self.browseFileFoldercheckBox = QtGui.QCheckBox(AptOfflineQtInstall)
            self.browseFileFoldercheckBox.setGeometry(QtCore.QRect(322, 10, 101, 18))
            self.browseFileFoldercheckBox.setObjectName(_fromUtf8("browseFileFoldercheckBox"))
    
            self.retranslateUi(AptOfflineQtInstall)
            QtCore.QMetaObject.connectSlotsByName(AptOfflineQtInstall)
    
        def retranslateUi(self, AptOfflineQtInstall):
            AptOfflineQtInstall.setWindowTitle(QtGui.QApplication.translate("AptOfflineQtInstall", "Install Packages", None, QtGui.QApplication.UnicodeUTF8))
            self.browseFilePathButton.setText(QtGui.QApplication.translate("AptOfflineQtInstall", "Browse", None, QtGui.QApplication.UnicodeUTF8))
            self.startInstallButton.setText(QtGui.QApplication.translate("AptOfflineQtInstall", "Install", None, QtGui.QApplication.UnicodeUTF8))
            self.cancelButton.setText(QtGui.QApplication.translate("AptOfflineQtInstall", "Close", None, QtGui.QApplication.UnicodeUTF8))
            self.label.setText(QtGui.QApplication.translate("AptOfflineQtInstall", "Specify file or folder path", None, QtGui.QApplication.UnicodeUTF8))
            self.label_2.setText(QtGui.QApplication.translate("AptOfflineQtInstall", "Status:", None, QtGui.QApplication.UnicodeUTF8))
            self.progressStatusDescription.setText(QtGui.QApplication.translate("AptOfflineQtInstall", "Ready", None, QtGui.QApplication.UnicodeUTF8))
            self.bugReportsButton.setText(QtGui.QApplication.translate("AptOfflineQtInstall", "Bug Reports", None, QtGui.QApplication.UnicodeUTF8))
            self.browseFileFoldercheckBox.setText(QtGui.QApplication.translate("AptOfflineQtInstall", "Is Directory", None, QtGui.QApplication.UnicodeUTF8))
    
    import resources_rc
    apt-offline-1.3.1/apt_offline_gui/AptOfflineQtFetch.py0000644000000000000000000003434212157275151017634 0ustar  # -*- coding: utf-8 -*-
    import os, sys
    from PyQt4 import QtCore, QtGui
    from PyQt4.QtGui import QMessageBox
    
    from apt_offline_gui.Ui_AptOfflineQtFetch import Ui_AptOfflineQtFetch
    from apt_offline_gui.UiDataStructs import GetterArgs
    from apt_offline_gui import AptOfflineQtCommon as guicommon
    import apt_offline_core.AptOfflineCoreLib
    
    from apt_offline_gui.AptOfflineQtFetchOptions import AptOfflineQtFetchOptions
    class Worker(QtCore.QThread):
        def __init__(self, parent = None):
            QtCore.QThread.__init__(self, parent)
            self.parent = parent
            self.exiting = False
    
        def __del__(self):
            self.exiting = True
            self.wait()
    
        def run(self):
            # setup i/o redirects before call
            sys.stdout = self
            sys.stderr = self 
            apt_offline_core.AptOfflineCoreLib.fetcher(self.args)
    
        def setArgs (self,args):
            self.args = args
    
        def write(self, text):
            # redirects console output to our consoleOutputHolder
            # extract chinese whisper from text
            if apt_offline_core.AptOfflineCoreLib.guiTerminateSignal:
                # ^ so artificial, the threads still remain frozen in time I suppose
                return
                
            if ("MSG_START" in text):
                self.emit (QtCore.SIGNAL('status(QString)'), "Fetching missing meta data ...")
            elif ("MSG_END" in text):
                self.emit (QtCore.SIGNAL('status(QString)'), "Downloading packages ...")
            elif ("WARNING" in text):
                self.emit (QtCore.SIGNAL('output(QString)'), 
                                        guicommon.style(text,"red"))
            elif ("Downloading" in text):
                self.emit (QtCore.SIGNAL('output(QString)'), 
                                        guicommon.style(text,"orange"))
            elif ("done." in text):
                self.emit (QtCore.SIGNAL('output(QString)'), 
                                        guicommon.style(text,"green"))
            elif ("[" in text and "]" in text):
                try:
                    # no more splits, we know the exact byte count now
                    progress = str(apt_offline_core.AptOfflineCoreLib.totalSize[1])
                    total = str(apt_offline_core.AptOfflineCoreLib.totalSize[0])
                    self.emit (QtCore.SIGNAL('progress(QString,QString)'), progress,total)
                except:
                    ''' nothing to do '''
            else:
                self.emit (QtCore.SIGNAL('output(QString)'), text.strip())
    
        def flush(self):
            ''' nothing to do :D '''
    
        def quit(self):
            self.emit (QtCore.SIGNAL('finished()'))
    
    
    class AptOfflineQtFetch(QtGui.QDialog):
        def __init__(self, parent=None):
            QtGui.QWidget.__init__(self, parent)
            self.ui = Ui_AptOfflineQtFetch()
            self.ui.setupUi(self)
            self.advancedOptionsDialog = AptOfflineQtFetchOptions()
            
            # Connect the clicked signal of the Signature File Browse button to it's slot
            QtCore.QObject.connect(self.ui.browseFilePathButton, QtCore.SIGNAL("clicked()"),
                            self.popupDirectoryDialog )
            
            # Connect the clicked signal of the Zip File Browse button to it's slot
            QtCore.QObject.connect(self.ui.browseZipFileButton, QtCore.SIGNAL("clicked()"),
                            self.popupZipFileDialog )
                                                    
            # Connect the clicked signal of the Save to it's Slot - accept
            QtCore.QObject.connect(self.ui.startDownloadButton, QtCore.SIGNAL("clicked()"),
                            self.StartDownload )
                            
            # Connect the clicked signal of the Cancel to it's Slot - reject
            QtCore.QObject.connect(self.ui.cancelButton, QtCore.SIGNAL("clicked()"),
                            self.handleCancel )
                            
            QtCore.QObject.connect(self.ui.profileFilePath, QtCore.SIGNAL("textChanged(QString)"),
                            self.controlStartDownloadBox )
    
            QtCore.QObject.connect(self.ui.profileFilePath, QtCore.SIGNAL("textChanged(QString)"),
                            self.controlStartDownloadBox )
            QtCore.QObject.connect(self.ui.zipFilePath, QtCore.SIGNAL("textChanged(QString)"),
                            self.controlStartDownloadBox )
            QtCore.QObject.connect(self.ui.zipFilePath, QtCore.SIGNAL("textChanged(QString)"),
                            self.controlStartDownloadBox )
            
            QtCore.QObject.connect(self.ui.advancedOptionsButton, QtCore.SIGNAL("clicked()"),
                            self.showAdvancedOptions )
            
            
            
            self.worker = Worker(parent=self)
            QtCore.QObject.connect(self.worker, QtCore.SIGNAL("output(QString)"),
                            self.updateLog )
            QtCore.QObject.connect(self.worker, QtCore.SIGNAL("progress(QString,QString)"),
                            self.updateProgress )
            QtCore.QObject.connect(self.worker, QtCore.SIGNAL("status(QString)"),
                            self.updateStatus )
            QtCore.QObject.connect(self.worker, QtCore.SIGNAL("finished()"),
                            self.finishedWork )
            QtCore.QObject.connect(self.worker, QtCore.SIGNAL("terminated()"),
                            self.finishedWork )
            
    
            #INFO: inform CLI that it's a gui app
            apt_offline_core.AptOfflineCoreLib.guiBool = True
            # Reduce extra line gaps in CLI o/p
            apt_offline_core.AptOfflineCoreLib.LINE_OVERWRITE_SMALL=""
            apt_offline_core.AptOfflineCoreLib.LINE_OVERWRITE_MID=""
            apt_offline_core.AptOfflineCoreLib.LINE_OVERWRITE_FULL=""
    
        def showAdvancedOptions(self):
                self.advancedOptionsDialog.show()
        
        def popupDirectoryDialog(self):
            # Popup a Directory selection box
            directory = QtGui.QFileDialog.getOpenFileName(self, u'Select the signature file')
            # Show the selected file path in the field marked for showing directory path
            self.ui.profileFilePath.setText(directory)
            
            self.controlStartDownloadBox()
        
        def popupZipFileDialog(self):
            
            if self.ui.saveDatacheckBox.isChecked() is True:
                    filename = QtGui.QFileDialog.getExistingDirectory(self, u'Select the folder to save downlaods to')
            else:
                    # Popup a Zip File selection box
                    filename = QtGui.QFileDialog.getSaveFileName(self, u'Select the zip file to save downloads')
            
            # Show the selected file path in the field marked for showing directory path
            self.ui.zipFilePath.setText(filename)
            
            self.controlStartDownloadBox()
            
        def StartDownload(self):
            # Do all the download related work here and then close
    
            # Clear the consoleOutputHolder
            self.ui.rawLogHolder.setText("")
            
            self.filepath = str(self.ui.profileFilePath.text())
    
            if os.path.isfile(self.filepath) == False:
                if (len(self.filepath) == 0):
                    self.ui.rawLogHolder.setText ( \
                        guicommon.style("Please select a signature file!",'red'))
                else:
                    self.ui.rawLogHolder.setText ( \
                        guicommon.style("%s does not exist." % self.filepath,'red'))
                return
            
            # TODO: check for zip file's presence
            self.zipfilepath = str(self.ui.zipFilePath.text())
            
            
            if os.path.isfile(self.zipfilepath):
                    # if file has write permission
                    if os.access(os.path.dirname(self.zipfilepath), os.W_OK) == False:
                        if (len(self.zipfilepath) == 0):
                            guicommon.updateInto (self.ui.rawLogHolder, 
                                        guicommon.style("Please select a zip file to create archive!",'red'))
                        else:
                            guicommon.updateInto (self.ui.rawLogHolder, 
                                guicommon.style("%s does not have write access." % self.zipfilepath,'red'))
                        return
                    
                    # if file already exists
                    if os.path.exists(self.zipfilepath):
                            ret = QMessageBox.warning(self, "Replace archive file?",
                               "The file %s already exists.\n"
                                  "Do you want to overwrite it?" % self.zipfilepath,
                                       QMessageBox.Yes | QMessageBox.No
                                       , QMessageBox.Yes)
                            if ret == QMessageBox.Yes:
                                # delete the file
                                try:
                                    os.remove(self.zipfilepath)
                                except:
                                    guicommon.updateInto (self.ui.rawLogHolder, 
                                        guicommon.style("Could'nt write to %s!" % self.zipfilepath,'red'))
                            else:
                                return
                        
                    targetFilePath = self.zipfilepath
                    targetDirPath = None
                        
            elif os.path.isdir(self.zipfilepath):
                    # if path has write permission
                    if os.access(os.path.dirname(self.zipfilepath), os.W_OK) == False:
                        if (len(self.zipfilepath) == 0):
                            guicommon.updateInto (self.ui.rawLogHolder, 
                                        guicommon.style("Please select a folder",'red'))
                        else:
                            guicommon.updateInto (self.ui.rawLogHolder, 
                                guicommon.style("%s does not have write access." % self.zipfilepath,'red'))
                        return
                    
    
                    targetFilePath = None
                    targetDirPath = self.zipfilepath
            else:
                    print "Invalid Path"
                    return False
    
            
            args = GetterArgs(filename=self.filepath, bundle_file=targetFilePath, progress_bar=self.ui.statusProgressBar, 
                            progress_label=self.ui.progressStatusDescription, proxy_host=self.advancedOptionsDialog.proxy_host,
                            proxy_port=self.advancedOptionsDialog.proxy_port, num_of_threads=self.advancedOptionsDialog.num_of_threads,
                            socket_timeout=self.advancedOptionsDialog.socket_timeout, cache_dir=self.advancedOptionsDialog.cache_dir,
                            download_dir=targetDirPath, disable_md5check=self.advancedOptionsDialog.disable_md5check,
                            deb_bugs=self.advancedOptionsDialog.deb_bugs)
            
            #returnStatus = apt_offline_core.AptOfflineCoreLib.fetcher(args)
            # TODO: deal with return status laters
            
            self.ui.cancelButton.setText("Cancel")
            self.disableAction()
            self.disableAtDownload()
            self.worker.setArgs (args)
            self.worker.start()
            
            #if (returnStatus):
            ''' TODO: do something with self.zipfilepath '''
                
            # TODO to be implemented later
            # self.accept()
    
        def updateLog(self,text):
            if not ('[' in text and ']' in text):
                if ('Downloaded data ' in text):
                    guicommon.updateInto (self.ui.rawLogHolder,
                                        guicommon.style(text,'green_fin'))
                    self.ui.progressStatusDescription.setText('Finished.')
                else:
                    guicommon.updateInto (self.ui.rawLogHolder,text)
    
        def updateStatus(self,text):
            self.ui.progressStatusDescription.setText(text)
    
        def updateProgress(self,progress,total):
            try:
                # try parsing numbers and updating progressBar
                percent = (float(progress)/float(total))*100
                self.ui.statusProgressBar.setValue (percent)
            except:
                ''' nothing to do '''
    
        def controlStartDownloadBox(self):
            if self.ui.profileFilePath.text().isEmpty():
                self.disableAction()
            if self.ui.zipFilePath.text().isEmpty():
                self.disableAction()
            else:
                self.enableAction()
    
        def handleCancel(self):
            if self.ui.cancelButton.text() == "Cancel":
                if self.worker.isRunning():
                    # Download is still in progress
                    ret = QMessageBox.warning(self, "Cancel current downloads?",
                        "A download is already in progress.\nDo you want to cancel it?",
                               QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
                    if ret == QMessageBox.Yes:
                        # we can't just stop threads, we need to pass message
                        apt_offline_core.AptOfflineCoreLib.guiTerminateSignal=True
                        self.updateStatus(guicommon.style("Download aborted","red"))
                        self.enableAtStop()
                        self.ui.cancelButton.setText("Close")
                else:
                    self.reject()
            else:
                self.reject()
    
        def resetUI(self):
            apt_offline_core.AptOfflineCoreLib.guiTerminateSignal=False
            apt_offline_core.AptOfflineCoreLib.guiMetaCompleted=False
            apt_offline_core.AptOfflineCoreLib.errlist = []
            apt_offline_core.AptOfflineCoreLib.totalSize = [0,0]
            self.ui.profileFilePath.setText("")
            self.ui.zipFilePath.setText("")
            self.ui.rawLogHolder.setText("")
            self.ui.statusProgressBar.setValue(0)
            self.updateStatus("Ready")
            self.enableAction()
            self.enableAtStop()
    
        def disableAction(self):
            self.ui.startDownloadButton.setEnabled(False)
        
        def disableAtDownload(self):
            self.ui.advancedOptionsButton.setEnabled(False)
            self.ui.browseZipFileButton.setEnabled(False)
            self.ui.browseFilePathButton.setEnabled(False)
            self.ui.zipFilePath.setEnabled(False)
            self.ui.profileFilePath.setEnabled(False)
            self.ui.saveDatacheckBox.setEnabled(False)
                
        def enableAction(self):
            self.ui.startDownloadButton.setEnabled(True)
    
        def enableAtStop(self):
            self.ui.advancedOptionsButton.setEnabled(True)
            self.ui.browseZipFileButton.setEnabled(True)
            self.ui.browseFilePathButton.setEnabled(True)
            self.ui.zipFilePath.setEnabled(True)
            self.ui.profileFilePath.setEnabled(True)
            self.ui.saveDatacheckBox.setEnabled(True)
    
        def finishedWork(self):
            ''' do nothing '''
            self.ui.cancelButton.setText("Close")
    
    if __name__ == "__main__":
        app = QtGui.QApplication(sys.argv)
        myapp = AptOfflineQtFetch()
        myapp.show()
        sys.exit(app.exec_())
    
    
    apt-offline-1.3.1/apt_offline_gui/genui.sh0000755000000000000000000000111712157275151015414 0ustar  #!/bin/sh
    # Todo : after adding a new UI file to dialig, also add
    #        its corresponding Ui_ script generator here
    #
    
    echo "Compiling Ui files"
    pyuic4 AptOfflineQtMain.ui > Ui_AptOfflineQtMain.py
    pyuic4 AptOfflineQtCreateProfile.ui > Ui_AptOfflineQtCreateProfile.py
    pyuic4  AptOfflineQtFetch.ui > Ui_AptOfflineQtFetch.py
    pyuic4 AptOfflineQtInstall.ui > Ui_AptOfflineQtInstall.py
    pyuic4 AptOfflineQtAbout.ui > Ui_AptOfflineQtAbout.py
    pyuic4  AptOfflineQtFetchOptions.ui > Ui_AptOfflineQtFetchOptions.py
    pyuic4  AptOfflineQtInstallBugList.ui > Ui_AptOfflineQtInstallBugList.py
    echo "Done"
    apt-offline-1.3.1/apt_offline_gui/Ui_AptOfflineQtInstallBugList.py0000644000000000000000000000570012125763750022136 0ustar  # -*- coding: utf-8 -*-
    
    # Form implementation generated from reading ui file 'AptOfflineQtInstallBugList.ui'
    #
    # Created: Mon Dec 31 16:02:37 2012
    #      by: PyQt4 UI code generator 4.9.3
    #
    # WARNING! All changes made in this file will be lost!
    
    from PyQt4 import QtCore, QtGui
    
    try:
        _fromUtf8 = QtCore.QString.fromUtf8
    except AttributeError:
        _fromUtf8 = lambda s: s
    
    class Ui_AptOfflineQtInstallBugList(object):
        def setupUi(self, AptOfflineQtInstallBugList):
            AptOfflineQtInstallBugList.setObjectName(_fromUtf8("AptOfflineQtInstallBugList"))
            AptOfflineQtInstallBugList.resize(642, 674)
            sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
            sizePolicy.setHorizontalStretch(0)
            sizePolicy.setVerticalStretch(0)
            sizePolicy.setHeightForWidth(AptOfflineQtInstallBugList.sizePolicy().hasHeightForWidth())
            AptOfflineQtInstallBugList.setSizePolicy(sizePolicy)
            AptOfflineQtInstallBugList.setModal(True)
            self.bugListViewWindow = QtGui.QListWidget(AptOfflineQtInstallBugList)
            self.bugListViewWindow.setGeometry(QtCore.QRect(30, 40, 581, 81))
            self.bugListViewWindow.setObjectName(_fromUtf8("bugListViewWindow"))
            self.closeButton = QtGui.QPushButton(AptOfflineQtInstallBugList)
            self.closeButton.setGeometry(QtCore.QRect(500, 630, 107, 24))
            icon = QtGui.QIcon()
            icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/icons/icons/application-exit.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
            self.closeButton.setIcon(icon)
            self.closeButton.setObjectName(_fromUtf8("closeButton"))
            self.label = QtGui.QLabel(AptOfflineQtInstallBugList)
            self.label.setGeometry(QtCore.QRect(30, 10, 231, 16))
            self.label.setObjectName(_fromUtf8("label"))
            self.bugListplainTextEdit = QtGui.QPlainTextEdit(AptOfflineQtInstallBugList)
            self.bugListplainTextEdit.setGeometry(QtCore.QRect(30, 160, 581, 441))
            self.bugListplainTextEdit.setAcceptDrops(False)
            self.bugListplainTextEdit.setReadOnly(True)
            self.bugListplainTextEdit.setObjectName(_fromUtf8("bugListplainTextEdit"))
    
            self.retranslateUi(AptOfflineQtInstallBugList)
            QtCore.QMetaObject.connectSlotsByName(AptOfflineQtInstallBugList)
    
        def retranslateUi(self, AptOfflineQtInstallBugList):
            AptOfflineQtInstallBugList.setWindowTitle(QtGui.QApplication.translate("AptOfflineQtInstallBugList", "List of Bugs", None, QtGui.QApplication.UnicodeUTF8))
            self.closeButton.setToolTip(QtGui.QApplication.translate("AptOfflineQtInstallBugList", "

    Close this window

    ", None, QtGui.QApplication.UnicodeUTF8)) self.closeButton.setText(QtGui.QApplication.translate("AptOfflineQtInstallBugList", "Close", None, QtGui.QApplication.UnicodeUTF8)) self.label.setText(QtGui.QApplication.translate("AptOfflineQtInstallBugList", "List of bugs", None, QtGui.QApplication.UnicodeUTF8)) import resources_rc apt-offline-1.3.1/apt_offline_gui/AptOfflineQtSaveZip.ui0000644000000000000000000000455711465541222020152 0ustar SaveZipFile Qt::WindowModal 0 0 445 176 Packages downloaded successfully 20 70 291 31 20 0 401 71 Success! The packages have been downloaded. Please specify the location where you want the zip file of the packages to be saved true 330 70 101 31 Browse 90 120 121 31 Save :/icons/icons/document-save.png:/icons/icons/document-save.png 240 120 121 31 Cancel :/icons/icons/dialog-cancel.png:/icons/icons/dialog-cancel.png apt-offline-1.3.1/apt_offline_gui/AptOfflineQtAbout.ui0000644000000000000000000002132212125763750017636 0ustar AboutAptOffline Qt::ApplicationModal 0 0 526 378 0 0 526 378 526 378 About Apt-Offline 12 30 511 21 16 Apt-Offline Qt::AlignCenter 7 90 510 241 3 About 10 20 491 31 apt-offline is an Offline APT Package Manager for Debian and derivatives. Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true 10 60 481 111 apt-offline can fully update/upgrade your disconnected Debian box without the need of connecting it to the network. This is a Graphical User Interface which exposes the functionality of apt-offline. true true Author 10 10 111 16 Written by: 30 30 271 16 Ritesh Raj Sarraf <rrs@researchut.com> 10 60 131 16 GUI written by: 30 80 261 16 Manish Sinha <mail@manishsinha.net> 30 100 271 16 Abhishek Mishra <ideamonk@gmail.com> Thanks To 10 10 221 16 Peter Otten 10 30 141 16 Duncan Booth 10 50 161 16 Simon Forman 10 70 161 16 Dennis Lee Bieber 10 110 471 51 The awesome Directi people for their office space required for the mini hackfests true true License 4 4 490 203 8 false false true GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. apt-offline is Copyright (C) - Ritesh Raj Sarraf 10 60 511 16 A GUI for apt-offline - an offline APT Package Manager Qt::AlignCenter 416 340 101 31 Close :/icons/icons/dialog-cancel.png:/icons/icons/dialog-cancel.png pushButton clicked() AboutAptOffline close() 258 322 161 178 apt-offline-1.3.1/apt_offline_gui/icons/0000755000000000000000000000000011547566433015071 5ustar apt-offline-1.3.1/apt_offline_gui/icons/contact-new.png0000644000000000000000000000367211465541222020015 0ustar PNG  IHDR szzsBIT|d pHYsvv}ՂtEXtSoftwarewww.inkscape.org<7IDATx[l\G3眽xכv4J&A*)(< ED* !hx*) $*ǎ7^v.3sv׉@h$O3:3ofYQB$H]# 0Y<2⌰ GƏ%ߙS)ޜ{5__Яj '!;Ϝ]]xʣVd" &V'8i?c yEd3w'H00Qɝ'99z Y`T;#il &/maM:MX)/jyxOu a{,"y7FSߘ71*ջ4Q͡PV 6cC75}=- ">6JA?;B$KXڼ`f<+fQ 84DwOveA}gSel74w_p56"c#I3*ʧWy|f_ TULe C&nҮCEiRCHĆ1S7'F'l qp i>yZЙ 0v5@R7 ~@[^Ęklځ>-,sN5^M@`B G>:').aDst 0xr)v8@a5L‚ 30sa0^\ DLJАٔA 4J̒zS !y!"׆! $Z9;+9[w`a}5 A[XZln\zoѡLSSUV 7y|qHꇲ 8IB ,w>פ‘ap"!^NnMcb G)]VuqgәC\|LP641q,?ƇwH&th:Zbq3qmxisHSkplOX4\JcV*JgEwSuzV4s<{C, {C)*<% %$-e%ë^w-`ppzRH{>B`FK)j8Ir=0G$yZtc,NucOϼrD  O _vec}>n΅R=V`Iպ!hU/8v5G>E3iO?\>P.7W-1Ӡ9jbx/ӈVP~SݽSB }ۺ듓G%'ťWli΄`\~U?Z.` n"Z&#F۳TIENDB`apt-offline-1.3.1/apt_offline_gui/icons/go-down.png0000644000000000000000000000263511465541222017143 0ustar PNG  IHDR szz pHYs^tIME  ;+Ck?@/ * F yٖ7mt<~|d|&L`ќsMK}SCHJT-hc;{fQY{}3- hnfD"V_Ns:2}hΐcȭLYض):ɸEh5+Ʀhp Q NdMs H߇3ЀMo@]j䉣*GN7ei%LH3Պ(~2'ϵQBSd/fR9ӂ˽h@Npu±BUTSs#APqr>fn&䍡|_pmF8x(‘"B]uM7YZm@VDdS&k,nN"$:z!NGv| ` ̅isT$'[7ڿ%fR e+*ʡpaJ d0ȺC ڪ,(=} *%vy;H]d @J>,EQ՞;hO92AF I;i>"{ț4Yԍ+Vc!CQ N *RG۩CnuT @bh&ܦOQQ ZWKu.w~fO< z%bCU Q`\38wH޻ =~3<(.t 4d͗dQ|39drjՍs]\ׅ8Ǹws'J ;խR}@Ԍn?;]?&CT5#hz_>bΦoi-O6z{钵zLmA(B5MGN}"$P38֏tLD߇5RLR}ӵIENDB`apt-offline-1.3.1/apt_offline_gui/icons/install.png0000644000000000000000000000357011465541222017236 0ustar PNG  IHDR szzsBIT|d pHYsvv}ՂtEXtSoftwarewww.inkscape.org<IDATxŗ PU²v d]G"52PBE_ddXJ!4 AU†NδfQ;|sKCOtrrZ>{{?mO//o.ruh3bUX~[Q, g4-=4q}P;WMmL_kl!+ۡS0H9JNg |%:1)IIIc[jk'`'`"cillL,Ǡ='<<| tvqXTɜc70_11<Cd[k,;IdddƍNTeB\B%'AF6NGA`rUEݩHmOBܓ;%xapz_δ$gϦx-ZǂBKyfd= [,7-CqS 9er=o84&(S}ݾ)YҭO~K BZ{hV@ՎjO[(? x~xx|d+S1 euЙ" weNw ;}G%%An E?qWcWgquytnֵͷ/G[ᰧ<7/=Rb.uqqDzݎ@Pŋ@xIq QOO ~~~OQ@ g}\:fl+`WdBxRh`)L&訲377E!""|k5&` FGcD$à>puǸ*E4cSw6G(&K])t<Tm&g|@7\7M] vb1CQk1 "88a.y.Y~0~,hB#R I>puu[[6/ZPpF))k ' 0uT);c3:511!W0!1`eeE;cb BUgk*#CP .NcZʂIENDB`apt-offline-1.3.1/apt_offline_gui/icons/configure.png0000644000000000000000000000265211547566433017565 0ustar PNG  IHDR szzsRGB pHYs^tIME)%FUbKGD*IDATxڽV[PSW5h"be4"2EBCx"Ũ!!ommVU:-XEZ|ؖ)Y?3k}]{a#\]gX+Tx+ߴ;_[{~6c wX]ߏALxxNdɉZVr{zLx^PkT֕exl%0j^S]x%hA%pX+˷ogx\ֻg|dm~DUH^~?NY,cPyF7}ϋ=\il*R+֌&aUm<R{kw?K&^C孺sƝϻIG; U؜ۛ-?XL}4g 5]Pռpb9 ^fZi91NHJ8%zދ|QW_gNn(; V3D{6ZDeuo?3g0w\D?6T|Yj6B:u-tK LH0W=vMFgY.򱯩9`2 hE a}ޭ5DD"kx[P^(b gJ3p8D[EIpVwOn"x׏44DigƲbQCG܎Gq#k~/q* #0m4T@&˿FX%%ӕ?]j%asj}fɢ%* ˅Bxzy".>e.zp<ٰٔqR S ((sppHa<+ ާǏ6bBD vvv* =+~ya 77BJj D$i^{Ξ'$V:h2 3T b"߅ȊD\7> r]ZT`Qkr7 t|V$$&@* "CA5-2\TkG0`^$ 'Px1[TʷhEѱSJھ}!Q{mmmAwG_:4ǩץBG?4Gv/<4\_22:cbm4$Ȃ PъOtSq1u+ ^]1.Bi5' "ڎZ]Y95}Qw<2-HZMU.BpL5WDd<`u!9 hQt%a 'C׃K/0S]['=jO~z ŠV+8[I}k@X^?):50Mjқο0\`ⱊ/+0.saST, ,_ CZŗtܸyV(>2o3 ON( KS((N)x QKD-(dg0!#jj:   l@G$ +L"({4tࢢ8>|q$ +¢K $9_BdEdM FX_/z`V ǁ1Z2a]4M/I |9ߞv>46˗.}P־RU-E&ωP( Y+2Mv,^lULƣ?766~gx0 9=BjDП>(h*]2ѐC4wh4<"+츮빎(]a'/8"06}۱gxX`:Лap 壻f;4GTOD#K@4յ^xψȝL&5pgߚғCIENDB`apt-offline-1.3.1/apt_offline_gui/icons/help-about.png0000644000000000000000000000272311465541222017627 0ustar PNG  IHDR szzsBIT|d pHYsvv}ՂtEXtSoftwarewww.inkscape.org<PIDATxŗ_U?sf{]][5ET,jTfH_֊z!zAY% %!!LEBP衤V(T1 kg=9x9s眙k- {t~^dÈBXu|IbR[ ߬Ǫ ,sưc~kK\^$0NK@$n#mWomlCԪϚeX0YY0,*\e^ŁS,TATOru=}+PjUSV^x!>TTJ!=}ݠ* ?iƁf7gҜ@$n|;+i#O l"6?\a\blbIA*bnv\I䳞\nՀM>LĂP(HÜM'5N.bT \UPqՂzJBU0P!krVcKY0.[-ך6 ŪzJpI,@{0\q4#Ǭli 1l%ٳ$fV$ f$8!w !w,F|x=L-J:!z^H1B5"P/4P~/u\[)u:$vBQkQa隇 <uXV#3`.:*v\/SW`rNڮ=@ {ڂt*2Z9edd?~l[`)`-CmC`mb1ۺ{iC٣ MuOsa 4RЏUo^^Rõ[yaZwƲGiXًHk%4Ua3Ԑ.|wԎ%(bAĬg vWzY:ap" M`xjF!:Am~;*X3cRnnuFfv * ,m@.d6#gF̞pOׯ(%JG( +DqM$DpL E5F L@*r_UIENDB`apt-offline-1.3.1/apt_offline_gui/icons/help-contents.png0000644000000000000000000000307711465541222020355 0ustar PNG  IHDR szzsBIT|d pHYsvv}ՂtEXtSoftwarewww.inkscape.org<IDATxW͏EUu|- +, q#~  I8p LL7Op7z0xMEID #3Ntw^uRٕdϮ՛Y#LСC 0b☚{ Ȅ)RkM$[@9ҊPvM_z>'g˞WN>\[_ҵ90vx#lw<=$jX]hA RP3X^RJw?֔Ppuv%s L c4s\R ]W[k@J&k?ı-/4.mHaynDPk#Yxx$I| Lc{Oncka`ǓHKTmXTjTbg1rL?]KS5lK-O)u&L hݿ<R<DSƎ-9] 3|G+xq|C sxsNJP(/ӂFU#MLM$8wQo=pyo|pr?k97Ymp>vՍ(` "xm "Ё({`;mO|^ĈyvN$0 2_h~)厛3TЉrg!@p|P&1E(/…6oQM$Ձ$X#'`e)*DAe^R@14ZS)B֖:tw ?ut2m@% O0"fT DTct{W͵b>CZ \kaõ,, J#56\jbǜdΤf kCh;t<{+9fw>`ju xSWN*j׊S0pY^clw]ggBjD~Y,P:Ќ2YC. DW. FD1pp ل0*W$JGjQohpȸiه؏>$S2s̟"!@/^!hx!22Vŕ@Tx2]C+Ր(Uk>FٕL8CU(d z$6pzrځ; i% McOzhMJ|1]2 x \D58kJ1J^SߎͲ̾}34"/²~.x µ}CnKS[i"/"k}:vޱ["ֺ?99lgJ) TB)Xc׃ p) E);Lۏ' nN w0Fǥ b\bj @]ffԌ5XkoEU8˽:K?!h3A IENDB`apt-offline-1.3.1/apt_offline_gui/icons/dialog-cancel.png0000644000000000000000000000423711465541222020253 0ustar PNG  IHDR szzbKGD pHYs^tIME (x,IDATx՗kl[9;v8vnBӥkM*QFWtih4nӐ&}&1&m(c+m)Z'4t6$M\\v|n;= QS5\ĤiG-yϣhu O4Ųfӝ?{{gg >\| ?vٳzz~/:i*p?aDobD7oX Ũ#94hD(誊IsH!zeϞ+ka¦50QgGqr9[G߻`IWyMU Q }|Z/oX5bZu!z 0v씩3N^"w|$A~`e^߷Zx†K50KN Sywّz0kH |a-PDcZp dGUx&,c՗ZwW.;`iހKDoC؟Hy|wY 6.VIE>ùe&)XzaҊp3oVH݊wӰE(|:3B8\ɩ3Uy&-YB<| ;%n/. 4+'ct o6~=t(A>W0&.J (n,Y%Lt~]8Be)$!n7\>7tꀯTeD*Le2J5Cfu"5ʌƤI+6MgV27/c_T^+H@3T,$F-%dZxR G>藛7=Ҿi9jt6Tzrd) @5=?vWjcr1Q@[Mib( o, xYeE xMmrɰHv=@G h[ʽ w9R8Ͼxj:%ޠ\^nZ4yeD*[*,8!.LKDZ-|k J O.+Fdx+F 'O@"m𯁡\֏1F }Y }6}K'쮪Ѩ @vKL 1_蓚x.+@\[eZhI X{*+n4,ɜXr68|$R EIކs+_j~fEяnLQ쪝NDy I -HVGP- r RypE-3!6Ѱ`M/ޮ2x- -p1p`,lds&e{F<˦Y MJ!noW=zqy1x3i  GiZߚ5AEz$ , eѣ̌/K6\pClBHq r3@rj#oa:ϣIENDB`apt-offline-1.3.1/apt_offline_gui/icons/dialog-ok-apply.png0000644000000000000000000000230211465541222020551 0ustar PNG  IHDR szzsBIT|d pHYsvv}ՂtEXtSoftwarewww.inkscape.org<?IDATxmLuǿk)]kR,V!l&,qE3c6I M$_,."fD^LB$al"lS@Z JJ>7\~}H U_zwuGu91Rt+q0-ݫtF:%666n+oyfTmBÆ#ѓ %eؗzM&ʒkA0EB 32@v@_1֚]Ν -Y<rJL%+vnkrG[Ҧ~om_cɳo+xP$HI͸gp)lOfڮF !9[-HV (fH5N\v  $7Ֆɖ ,M$XqIZ҈bjb`:85rT;+y9hgW7׮% Brɀ"0HjHl"b(8> s O׌dCi{LeσBHR EQ(K0DY1i5W I u3OZq3ׇ. V ڇ-H0o Ej KNcƹ_/|EN2JL=h}'r ) W:8{AM $WBø}"T `1qLIMODBu|nX}l|{n 8p##\S:zVi4i܍jh ڊu.oIENDB`apt-offline-1.3.1/apt_offline_gui/icons/application-exit.png0000644000000000000000000000334011465541222021035 0ustar PNG  IHDR szz pHYs^tIME kbKGDmIDATxW[lTU]ޙR>JUbĆ"HH0 ["9=g̙ kSY+͋AMSLhU:;3}}f9-I4Zuu eԨ}'O&ޡ rzM0^]h65 Pшn6\߉D]=R |֏P!|uh‘A'͜938S"? ep;MJ29g{[1b*;Vś_@ h`#obrV$WO j (_{O}E,)ą!b6=IΙqX)d>[kBaB&ۖSU,bz {w ;UD2Il/&e"RD[Oq XKY(Uasze 'WQ-lm%e1pgU1P, w׆h zZ̲yPH" Ȥ !@@{RA|ϞGDLop:>  ?͓"*8c1!oj07:,Gb /pC>Z2YDÜ.ЭWh4W{Q#(Ep%MsШ x .1=6tl,Ϧ {t ENXqitDu8>Ў0n/ye{f7vDhIENDB`apt-offline-1.3.1/apt_offline_gui/AptOfflineQtInstall.ui0000644000000000000000000001146012125763750020174 0ustar AptOfflineQtInstall Qt::ApplicationModal 0 0 466 400 0 0 466 400 466 400 Install Packages 30 32 270 30 320 32 110 30 Browse false 31 75 130 30 Install :/icons/icons/dialog-ok-apply.png:/icons/icons/dialog-ok-apply.png 320 75 110 30 Close :/icons/icons/application-exit.png:/icons/icons/application-exit.png 30 146 410 20 0 30 2 200 30 10 Specify file or folder path 34 120 70 16 10 Status: 90 120 53 15 10 Ready 30 180 411 191 false 172 75 130 30 Bug Reports :/icons/icons/help-about.png:/icons/icons/help-about.png 322 10 101 18 Is Directory apt-offline-1.3.1/apt_offline_gui/resources.qrc0000644000000000000000000000070311547566433016477 0ustar icons/configure.png icons/help-about.png icons/application-exit.png icons/contact-new.png icons/dialog-cancel.png icons/dialog-ok-apply.png icons/document-save.png icons/go-down.png icons/help-contents.png icons/install.png apt-offline-1.3.1/apt_offline_gui/AptOfflineQtFetchOptions.py0000644000000000000000000000735612125763750021217 0ustar import sys, os from PyQt4 import QtCore, QtGui from apt_offline_gui.Ui_AptOfflineQtFetchOptions import Ui_downloadOptionsDialog class AptOfflineQtFetchOptions(QtGui.QDialog): def __init__(self, parent=None): QtGui.QWidget.__init__(self, parent) self.ui = Ui_downloadOptionsDialog() self.ui.setupUi(self) # Connect the clicked signal of the Ok button to it's slot QtCore.QObject.connect(self.ui.downloadOptionDialogOkButton, QtCore.SIGNAL("clicked()"), self.validateOptions ) QtCore.QObject.connect(self.ui.useProxyCheckBox, QtCore.SIGNAL("toggled(bool)"), self.toggleProxyControls ) QtCore.QObject.connect(self.ui.cacheDirBrowseButton, QtCore.SIGNAL("clicked()"), self.populateCacheDir ) # defaults self.num_of_threads = 1 self.socket_timeout = 30 self.cache_dir = None self.disable_md5check = False self.deb_bugs = False self.proxy_host = None self.proxy_port = None def storeOptions(self): self._num_of_threads = self.ui.spinThreads.value() self._socket_timeout = self.ui.spinTimeout.value() self._cache_dir = str(self.ui.cacheDirLineEdit.text() ) self._disable_md5check = self.ui.disableChecksumCheckBox.isChecked() self._deb_bugs = self.ui.fetchBugReportsCheckBox.isChecked() if self.ui.useProxyCheckBox.isChecked(): self._proxy_host = str(self.ui.proxyHostLineEdit.text() ) self._proxy_port = str(self.ui.proxyPortLineEdit.text() ) else: self._proxy_host = None self._proxy_port = None def validateOptions(self): self.storeOptions() if len(self._cache_dir) > 0 and not (os.access(self._cache_dir, os.W_OK) or os.access(self._cache_dir, os.R_OK) ): QtGui.QMessageBox.critical(self, "Error", "Could not locate cache directory") return if self._proxy_port: try: int(self._proxy_port) except: QtGui.QMessageBox.critical(self, "Error", "Invalid Proxy Port Number") return self.applyOptionValues() self.hide() def applyOptionValues(self): self.num_of_threads = self._num_of_threads self.socket_timeout = self._socket_timeout self.cache_dir = self._cache_dir self.disable_md5check = self._disable_md5check self.deb_bugs = self._deb_bugs self.proxy_host = self._proxy_host self.proxy_port = self._proxy_port def toggleProxyControls(self): if self.ui.useProxyCheckBox.isChecked(): self.ui.proxyHostLineEdit.setEnabled(True) self.ui.proxyPortLineEdit.setEnabled(True) else: self.ui.proxyHostLineEdit.setEnabled(False) self.ui.proxyPortLineEdit.setEnabled(False) def populateCacheDir(self): directory = QtGui.QFileDialog.getExistingDirectory(None, u'Provide path to APT\'s Cache Dir') self.ui.cacheDirLineEdit.setText(directory) self._cache_dir = directory if __name__ == "__main__": app = QtGui.QApplication(sys.argv) myapp = AptOfflineQtFetchOptions() myapp.show() sys.exit(app.exec_()) apt-offline-1.3.1/apt_offline_gui/Ui_AptOfflineQtFetchOptions.py0000644000000000000000000002342212125763750021644 0ustar # -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'AptOfflineQtFetchOptions.ui' # # Created: Mon Dec 31 16:02:37 2012 # by: PyQt4 UI code generator 4.9.3 # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui try: _fromUtf8 = QtCore.QString.fromUtf8 except AttributeError: _fromUtf8 = lambda s: s class Ui_downloadOptionsDialog(object): def setupUi(self, downloadOptionsDialog): downloadOptionsDialog.setObjectName(_fromUtf8("downloadOptionsDialog")) downloadOptionsDialog.resize(443, 304) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(downloadOptionsDialog.sizePolicy().hasHeightForWidth()) downloadOptionsDialog.setSizePolicy(sizePolicy) downloadOptionsDialog.setMinimumSize(QtCore.QSize(443, 304)) downloadOptionsDialog.setMaximumSize(QtCore.QSize(443, 304)) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/icons/icons/configure.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) downloadOptionsDialog.setWindowIcon(icon) downloadOptionsDialog.setSizeGripEnabled(False) downloadOptionsDialog.setModal(True) self.lblThreads = QtGui.QLabel(downloadOptionsDialog) self.lblThreads.setGeometry(QtCore.QRect(300, 10, 81, 30)) font = QtGui.QFont() font.setPointSize(10) self.lblThreads.setFont(font) self.lblThreads.setObjectName(_fromUtf8("lblThreads")) self.spinThreads = QtGui.QSpinBox(downloadOptionsDialog) self.spinThreads.setGeometry(QtCore.QRect(390, 9, 40, 30)) self.spinThreads.setMinimum(1) self.spinThreads.setMaximum(10) self.spinThreads.setObjectName(_fromUtf8("spinThreads")) self.downloadOptionDialogOkButton = QtGui.QPushButton(downloadOptionsDialog) self.downloadOptionDialogOkButton.setGeometry(QtCore.QRect(350, 260, 81, 31)) icon1 = QtGui.QIcon() icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/icons/icons/dialog-ok-apply.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.downloadOptionDialogOkButton.setIcon(icon1) self.downloadOptionDialogOkButton.setObjectName(_fromUtf8("downloadOptionDialogOkButton")) self.lblSocketTimeo = QtGui.QLabel(downloadOptionsDialog) self.lblSocketTimeo.setGeometry(QtCore.QRect(10, 8, 111, 30)) font = QtGui.QFont() font.setPointSize(10) self.lblSocketTimeo.setFont(font) self.lblSocketTimeo.setObjectName(_fromUtf8("lblSocketTimeo")) self.spinTimeout = QtGui.QSpinBox(downloadOptionsDialog) self.spinTimeout.setGeometry(QtCore.QRect(150, 7, 51, 30)) self.spinTimeout.setMinimum(10) self.spinTimeout.setMaximum(100) self.spinTimeout.setProperty("value", 30) self.spinTimeout.setObjectName(_fromUtf8("spinTimeout")) self.cacheDirLineEdit = QtGui.QLineEdit(downloadOptionsDialog) self.cacheDirLineEdit.setGeometry(QtCore.QRect(150, 60, 191, 31)) self.cacheDirLineEdit.setObjectName(_fromUtf8("cacheDirLineEdit")) self.cacheDirBrowseButton = QtGui.QPushButton(downloadOptionsDialog) self.cacheDirBrowseButton.setGeometry(QtCore.QRect(350, 60, 81, 31)) self.cacheDirBrowseButton.setObjectName(_fromUtf8("cacheDirBrowseButton")) self.lblCacheDir = QtGui.QLabel(downloadOptionsDialog) self.lblCacheDir.setGeometry(QtCore.QRect(10, 60, 141, 30)) font = QtGui.QFont() font.setPointSize(10) self.lblCacheDir.setFont(font) self.lblCacheDir.setObjectName(_fromUtf8("lblCacheDir")) self.disableChecksumCheckBox = QtGui.QCheckBox(downloadOptionsDialog) self.disableChecksumCheckBox.setGeometry(QtCore.QRect(10, 120, 151, 31)) self.disableChecksumCheckBox.setObjectName(_fromUtf8("disableChecksumCheckBox")) self.fetchBugReportsCheckBox = QtGui.QCheckBox(downloadOptionsDialog) self.fetchBugReportsCheckBox.setGeometry(QtCore.QRect(180, 120, 151, 31)) self.fetchBugReportsCheckBox.setObjectName(_fromUtf8("fetchBugReportsCheckBox")) self.proxyGroupBox = QtGui.QGroupBox(downloadOptionsDialog) self.proxyGroupBox.setGeometry(QtCore.QRect(10, 170, 441, 80)) self.proxyGroupBox.setStyleSheet(_fromUtf8("")) self.proxyGroupBox.setTitle(_fromUtf8("")) self.proxyGroupBox.setFlat(True) self.proxyGroupBox.setObjectName(_fromUtf8("proxyGroupBox")) self.proxyHostLineEdit = QtGui.QLineEdit(self.proxyGroupBox) self.proxyHostLineEdit.setEnabled(False) self.proxyHostLineEdit.setGeometry(QtCore.QRect(25, 39, 238, 31)) self.proxyHostLineEdit.setObjectName(_fromUtf8("proxyHostLineEdit")) self.proxyPortLineEdit = QtGui.QLineEdit(self.proxyGroupBox) self.proxyPortLineEdit.setEnabled(False) self.proxyPortLineEdit.setGeometry(QtCore.QRect(280, 39, 61, 31)) self.proxyPortLineEdit.setText(_fromUtf8("")) self.proxyPortLineEdit.setObjectName(_fromUtf8("proxyPortLineEdit")) self.useProxyCheckBox = QtGui.QCheckBox(self.proxyGroupBox) self.useProxyCheckBox.setGeometry(QtCore.QRect(0, 0, 89, 21)) font = QtGui.QFont() font.setBold(True) font.setWeight(75) self.useProxyCheckBox.setFont(font) self.useProxyCheckBox.setObjectName(_fromUtf8("useProxyCheckBox")) self.lblProxyPort = QtGui.QLabel(downloadOptionsDialog) self.lblProxyPort.setGeometry(QtCore.QRect(290, 186, 138, 30)) font = QtGui.QFont() font.setPointSize(10) self.lblProxyPort.setFont(font) self.lblProxyPort.setObjectName(_fromUtf8("lblProxyPort")) self.lblProxyHost = QtGui.QLabel(downloadOptionsDialog) self.lblProxyHost.setGeometry(QtCore.QRect(35, 186, 138, 30)) font = QtGui.QFont() font.setPointSize(10) self.lblProxyHost.setFont(font) self.lblProxyHost.setObjectName(_fromUtf8("lblProxyHost")) self.retranslateUi(downloadOptionsDialog) QtCore.QMetaObject.connectSlotsByName(downloadOptionsDialog) def retranslateUi(self, downloadOptionsDialog): downloadOptionsDialog.setWindowTitle(QtGui.QApplication.translate("downloadOptionsDialog", "Advanced options for download", None, QtGui.QApplication.UnicodeUTF8)) self.lblThreads.setToolTip(QtGui.QApplication.translate("downloadOptionsDialog", "Number of parallel connections", None, QtGui.QApplication.UnicodeUTF8)) self.lblThreads.setText(QtGui.QApplication.translate("downloadOptionsDialog", "Use threads", None, QtGui.QApplication.UnicodeUTF8)) self.spinThreads.setToolTip(QtGui.QApplication.translate("downloadOptionsDialog", "Number of parallel connections", None, QtGui.QApplication.UnicodeUTF8)) self.downloadOptionDialogOkButton.setText(QtGui.QApplication.translate("downloadOptionsDialog", "Ok", None, QtGui.QApplication.UnicodeUTF8)) self.lblSocketTimeo.setToolTip(QtGui.QApplication.translate("downloadOptionsDialog", "Nework timeout in seconds", None, QtGui.QApplication.UnicodeUTF8)) self.lblSocketTimeo.setText(QtGui.QApplication.translate("downloadOptionsDialog", "Network Timeout", None, QtGui.QApplication.UnicodeUTF8)) self.spinTimeout.setToolTip(QtGui.QApplication.translate("downloadOptionsDialog", "Nework timeout in seconds", None, QtGui.QApplication.UnicodeUTF8)) self.cacheDirLineEdit.setToolTip(QtGui.QApplication.translate("downloadOptionsDialog", "Cache folder to search for", None, QtGui.QApplication.UnicodeUTF8)) self.cacheDirBrowseButton.setText(QtGui.QApplication.translate("downloadOptionsDialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) self.lblCacheDir.setToolTip(QtGui.QApplication.translate("downloadOptionsDialog", "Cache folder to search for", None, QtGui.QApplication.UnicodeUTF8)) self.lblCacheDir.setText(QtGui.QApplication.translate("downloadOptionsDialog", "Cache Directory", None, QtGui.QApplication.UnicodeUTF8)) self.disableChecksumCheckBox.setToolTip(QtGui.QApplication.translate("downloadOptionsDialog", "Disables checksum verification of downloaded items. Enable only if you know what you are doing", None, QtGui.QApplication.UnicodeUTF8)) self.disableChecksumCheckBox.setText(QtGui.QApplication.translate("downloadOptionsDialog", "Disable Checksum", None, QtGui.QApplication.UnicodeUTF8)) self.fetchBugReportsCheckBox.setToolTip(QtGui.QApplication.translate("downloadOptionsDialog", "Fetch Bug Reports", None, QtGui.QApplication.UnicodeUTF8)) self.fetchBugReportsCheckBox.setText(QtGui.QApplication.translate("downloadOptionsDialog", "Fetch Bug Reports", None, QtGui.QApplication.UnicodeUTF8)) self.proxyHostLineEdit.setToolTip(QtGui.QApplication.translate("downloadOptionsDialog", "Proxy Server Host/IP Address", None, QtGui.QApplication.UnicodeUTF8)) self.proxyPortLineEdit.setToolTip(QtGui.QApplication.translate("downloadOptionsDialog", "Proxy Server Port Address", None, QtGui.QApplication.UnicodeUTF8)) self.useProxyCheckBox.setText(QtGui.QApplication.translate("downloadOptionsDialog", "Proxy", None, QtGui.QApplication.UnicodeUTF8)) self.lblProxyPort.setToolTip(QtGui.QApplication.translate("downloadOptionsDialog", "Cache folder to search for", None, QtGui.QApplication.UnicodeUTF8)) self.lblProxyPort.setText(QtGui.QApplication.translate("downloadOptionsDialog", "Port", None, QtGui.QApplication.UnicodeUTF8)) self.lblProxyHost.setToolTip(QtGui.QApplication.translate("downloadOptionsDialog", "Cache folder to search for", None, QtGui.QApplication.UnicodeUTF8)) self.lblProxyHost.setText(QtGui.QApplication.translate("downloadOptionsDialog", "Host", None, QtGui.QApplication.UnicodeUTF8)) import resources_rc apt-offline-1.3.1/apt_offline_gui/AptOfflineQtInstallBugList.py0000644000000000000000000001077012125763750021504 0ustar # -*- coding: utf-8 -*- import os,sys from PyQt4 import QtCore, QtGui import zipfile, tempfile from apt_offline_gui.Ui_AptOfflineQtInstallBugList import Ui_AptOfflineQtInstallBugList import apt_offline_core.AptOfflineCoreLib as AptOfflineCoreLib class AptOfflineQtInstallBugList(QtGui.QDialog): def __init__(self, filepath, parent=None): QtGui.QWidget.__init__(self, parent) self.ui = Ui_AptOfflineQtInstallBugList() self.bugList = {} self.filepath = filepath self.ui.setupUi(self) self.populateBugList(self.filepath) self.ui.bugListViewWindow.itemSelectionChanged.connect(self.populateBugListPlainTextEdit) # Connect the clicked signal of the Browse button to it's slot QtCore.QObject.connect(self.ui.closeButton, QtCore.SIGNAL("clicked()"), self.reject ) def populateBugListPlainTextEdit(self): self.ui.bugListplainTextEdit.clear() textItem = str(self.ui.bugListViewWindow.currentItem().text() ) extractedText = self.bugList[textItem] self.ui.bugListplainTextEdit.appendPlainText(" ".join(extractedText)) myCursor = self.ui.bugListplainTextEdit.textCursor() myCursor.movePosition(myCursor.Start) self.ui.bugListplainTextEdit.setTextCursor(myCursor) def noBugPopulateBugListPlainTextEdit(self): self.ui.bugListplainTextEdit.clear() self.ui.bugListplainTextEdit.appendPlainText("No Bug Reports Found") def populateBugList(self, path): if os.path.isfile(path): file = zipfile.ZipFile(path, "r") for filename in file.namelist(): if filename.endswith( AptOfflineCoreLib.apt_bug_file_format ): temp = tempfile.NamedTemporaryFile() temp.file.write( file.read( filename ) ) temp.file.flush() temp.file.seek( 0 ) #Let's go back to the start of the file for bug_subject_identifier in temp.file.readlines(): if bug_subject_identifier.startswith( '#' ): bug_subject_identifier = bug_subject_identifier.rstrip("\n") temp.file.seek(0) self.bugList[bug_subject_identifier] = temp.file.readlines() break temp.file.close() elif os.path.isdir(path): for filename in os.listdir( path ): if filename.endswith( AptOfflineCoreLib.apt_bug_file_format ): filename = os.path.join(path, filename) temp = open(filename, 'r') for bug_subject_identifier in temp.readlines(): if bug_subject_identifier.startswith( '#' ): bug_subject_identifier = bug_subject_identifier.rstrip("\n") temp.seek(0) self.bugList[bug_subject_identifier] = temp.readlines() break temp.close() else: print "Invalid Path" return False if len(self.bugList.keys()) is 0: self.noBugPopulateBugListPlainTextEdit() else: for eachItem in self.bugList.keys(): item = QtGui.QListWidgetItem(eachItem) self.ui.bugListViewWindow.addItem(item) if __name__ == "__main__": app = QtGui.QApplication(sys.argv) myapp = AptOfflineQtInstallBugList() myapp.show() sys.exit(app.exec_()) apt-offline-1.3.1/apt_offline_gui/AptOfflineQtMain.ui0000644000000000000000000001763211530057327017454 0ustar AptOfflineMain 0 0 432 544 0 0 432 544 432 544 APT Offline Qt::ToolButtonIconOnly true 30 20 371 40 12 true Generate Signature :/icons/icons/contact-new.png:/icons/icons/contact-new.png 30 80 371 41 12 50 false Download Packages or Updates :/icons/icons/go-down.png:/icons/icons/go-down.png 30 140 371 41 12 Install Packages or Updates :/icons/icons/install.png:/icons/icons/install.png 30 220 371 211 10 QFrame::Box QFrame::Plain 1 0 0 371 211 Hover your mouse over the buttons to get the description Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true 10 280 450 121 41 10 Exit :/icons/icons/application-exit.png:/icons/icons/application-exit.png 30 200 91 21 10 Description 0 0 432 25 Operations Help :/icons/icons/contact-new.png:/icons/icons/contact-new.png Generate Signature Ctrl+N :/icons/icons/go-down.png:/icons/icons/go-down.png Download Packages or Updates Ctrl+O :/icons/icons/install.png:/icons/icons/install.png Install Packages or Updates Ctrl+I :/icons/icons/application-exit.png:/icons/icons/application-exit.png Exit Ctrl+Q :/icons/icons/help-contents.png:/icons/icons/help-contents.png Help F1 :/icons/icons/help-about.png:/icons/icons/help-about.png About apt-offline apt-offline-1.3.1/apt_offline_gui/QtProgressBar.py0000644000000000000000000000605111465541222017054 0ustar class QtProgressBar( object ): def __init__( self, progressbar=None,label =None, minValue=0, maxValue=0, total_items=None): self.min = minValue self.max = maxValue self.span = float( self.max - self.min ) self.fd = fd self.signal_set = False # This field stores QProgessBar self.progressBar = progressbar # This field stores the Label self.progressLabel = label self.value = self.min if total_items is None or total_items <= 0: self.items = 0 #count of items being tracked self.items_update = True else: self.items = total_items self.items_update = False self.complete = 0 def handle_resize( self, signum, frame ): h, w = array( 'h', ioctl( self.fd, termios.TIOCGWINSZ, '\0' * 8 ) )[:2] self.width = w def updateValue( self, newValue ): #require caller to supply a value! newValue is the increment from last call self.value = max( self.min, min( self.max, self.value + newValue ) ) self.display() def completed( self ): self.complete = self.complete + 1 #if self.signal_set: #signal.signal( signal.SIGWINCH, signal.SIG_DFL ) self.display() def addItem( self, maxValue ): self.max = self.max + maxValue self.span = float( self.max - self.min ) if self.items_update is True: self.items = self.items + 1 self.display() def display( self ): #print "\r%3s /%3s items: %s\r" % ( self.complete, self.items, str( self ) ), self.progressBar.setValue(int(self.__str__())) progressText = "%3s /%3s Size: %s" % ( self.complete, self.items, self.__numStr__( self.max / 1024 )) self.progressLabel.setText(progressText) def __str__( self ): #compute display fraction percentFilled = ( ( self.value - self.min ) / self.span ) #widthFilled = int( self.width * percentFilled + 0.5 ) #return ( "[" + "#"*widthFilled + " " * ( self.width - widthFilled ) + "]" + " %5.1f%% of %s" % ( percentFilled * 100.0, self.__numStr__( self.max / 1024 ) ) ) return percentFilled def __numStr__( self, size ): if size > 1024: size = size / 1024 if size > 1024: size = size / 1024 return ( "%d GiB" % ( size ) ) return ( "%d MiB" % ( size ) ) return ( "%d KiB" % ( size ) ) apt-offline-1.3.1/apt_offline_gui/AptOfflineQtFetch.ui0000644000000000000000000001461712125763750017626 0ustar AptOfflineQtFetch Qt::ApplicationModal 0 0 468 483 0 0 468 475 468 495 Fetch Packages or Updates 30 35 270 30 320 35 110 30 Browse false 179 148 120 30 Download :/icons/icons/go-down.png:/icons/icons/go-down.png false false false 319 148 111 30 Close :/icons/icons/application-exit.png:/icons/icons/application-exit.png 30 5 200 30 10 Select the signature file 30 214 410 20 0 32 194 70 16 10 Status: 82 194 341 16 10 Ready 30 252 411 211 false true 320 98 110 30 Browse 30 68 200 30 10 Save data as 30 98 270 30 true 31 148 131 30 Additional options for download Options :/icons/icons/configure.png:/icons/icons/configure.png false false false 321 80 110 18 Is Directory apt-offline-1.3.1/apt_offline_gui/AptOfflineQtFetchOptions.ui0000644000000000000000000001752112125763750021177 0ustar downloadOptionsDialog 0 0 443 304 0 0 443 304 443 304 Advanced options for download :/icons/icons/configure.png:/icons/icons/configure.png false true 300 10 81 30 10 Number of parallel connections Use threads 390 9 40 30 Number of parallel connections 1 10 350 260 81 31 Ok :/icons/icons/dialog-ok-apply.png:/icons/icons/dialog-ok-apply.png 10 8 111 30 10 Nework timeout in seconds Network Timeout 150 7 51 30 Nework timeout in seconds 10 100 30 150 60 191 31 Cache folder to search for 350 60 81 31 Browse 10 60 141 30 10 Cache folder to search for Cache Directory 10 120 151 31 Disables checksum verification of downloaded items. Enable only if you know what you are doing Disable Checksum 180 120 151 31 Fetch Bug Reports Fetch Bug Reports 10 170 441 80 true false 25 39 238 31 Proxy Server Host/IP Address false 280 39 61 31 Proxy Server Port Address 0 0 89 21 75 true Proxy 290 186 138 30 10 Cache folder to search for Port 35 186 138 30 10 Cache folder to search for Host apt-offline-1.3.1/apt_offline_gui/AptOfflineQtAbout.py0000644000000000000000000000230411465541222017642 0ustar import os import sys from PyQt4 import QtCore, QtGui from apt_offline_gui.Ui_AptOfflineQtAbout import Ui_AboutAptOffline class AptOfflineQtAbout(QtGui.QDialog): def __init__(self, parent=None): QtGui.QWidget.__init__(self, parent) self.ui = Ui_AboutAptOffline() self.ui.setupUi(self) self.setupLicense() def setupLicense(self): ''' LICENSE is looked for in - 1. Current directory (dev / possibly windows) 2. /usr/local/share/doc/apt-offline (source install) 3. /usr/share/doc/apt-offline (package install) TODO: to resolve location on window ''' filename = 'LICENSE' locations = ['.', '/usr/local/share/doc/apt-offline/', '/usr/share/doc/apt-offline'] for l in locations: filepath = os.path.join(l,filename) if os.path.isfile(filepath): f = open(filepath,"r") self.ui.licenseText.setPlainText(f.read()) f.close() return if __name__ == "__main__": app = QtGui.QApplication(sys.argv) myapp = AptOfflineQtAbout() myapp.show() sys.exit(app.exec_()) apt-offline-1.3.1/apt_offline_gui/AptOfflineQtCommon.py0000644000000000000000000000130211465541222020015 0ustar # -*- coding: utf-8 -*- styles = { 'red': "#", 'orange': "#", 'green': "#", 'green_fin': "#" } def style(text, style_type): try: return styles[style_type].replace("#",text) except: return text def updateInto(myobject,text): # sanitize coloring if ('[1;' in text): return if ("ERROR" in text or "FATAL" in text): text = style(text,'red') if ("Completed" in text): text = style(text,'green_fin') myobject.append (text)apt-offline-1.3.1/apt_offline_gui/Ui_AptOfflineQtSaveZip.py0000644000000000000000000000525611465541222020617 0ustar # -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'AptOfflineQtSaveZip.ui' # # Created: Wed Feb 3 01:00:39 2010 # by: PyQt4 UI code generator 4.4.4 # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui class Ui_SaveZipFile(object): def setupUi(self, SaveZipFile): SaveZipFile.setObjectName("SaveZipFile") SaveZipFile.setWindowModality(QtCore.Qt.WindowModal) SaveZipFile.resize(445, 176) self.zipFilePath = QtGui.QLineEdit(SaveZipFile) self.zipFilePath.setGeometry(QtCore.QRect(20, 70, 291, 31)) self.zipFilePath.setObjectName("zipFilePath") self.label = QtGui.QLabel(SaveZipFile) self.label.setGeometry(QtCore.QRect(20, 0, 401, 71)) self.label.setWordWrap(True) self.label.setObjectName("label") self.browseFilePathButton = QtGui.QPushButton(SaveZipFile) self.browseFilePathButton.setGeometry(QtCore.QRect(330, 70, 101, 31)) self.browseFilePathButton.setObjectName("browseFilePathButton") self.saveButton = QtGui.QPushButton(SaveZipFile) self.saveButton.setGeometry(QtCore.QRect(90, 120, 121, 31)) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(":/icons/icons/document-save.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.saveButton.setIcon(icon) self.saveButton.setObjectName("saveButton") self.cancelButton = QtGui.QPushButton(SaveZipFile) self.cancelButton.setGeometry(QtCore.QRect(240, 120, 121, 31)) icon1 = QtGui.QIcon() icon1.addPixmap(QtGui.QPixmap(":/icons/icons/dialog-cancel.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.cancelButton.setIcon(icon1) self.cancelButton.setObjectName("cancelButton") self.retranslateUi(SaveZipFile) QtCore.QMetaObject.connectSlotsByName(SaveZipFile) def retranslateUi(self, SaveZipFile): SaveZipFile.setWindowTitle(QtGui.QApplication.translate("SaveZipFile", "Packages downloaded successfully", None, QtGui.QApplication.UnicodeUTF8)) self.label.setText(QtGui.QApplication.translate("SaveZipFile", "Success! The packages have been downloaded. Please specify the location where you want the zip file of the packages to be saved", None, QtGui.QApplication.UnicodeUTF8)) self.browseFilePathButton.setText(QtGui.QApplication.translate("SaveZipFile", "Browse", None, QtGui.QApplication.UnicodeUTF8)) self.saveButton.setText(QtGui.QApplication.translate("SaveZipFile", "Save", None, QtGui.QApplication.UnicodeUTF8)) self.cancelButton.setText(QtGui.QApplication.translate("SaveZipFile", "Cancel", None, QtGui.QApplication.UnicodeUTF8)) import resources_rc apt-offline-1.3.1/apt_offline_gui/UiDataStructs.py0000644000000000000000000000633411547566433017075 0ustar # -*- coding: utf-8 -*- from PyQt4 import QtCore, QtGui class SetterArgs(): def __init__(self, filename, update, upgrade, install_packages, simulate=False): self.set = filename # self.set_update is of type boolean self.set_update = update # self.set_upgrade can be either True or False self.set_upgrade = upgrade self.upgrade_type = "upgrade" # Should be set to None for disabling or Tuple for activating self.set_install_packages = install_packages # To be implmented later self.src_build_dep = False self.set_install_src_packages = None self.set_install_release = None self.simulate=simulate def __str__(self): print "self.set=",self.set print "self.set_update=",self.set_update print "self.set_upgrade=",self.set_upgrade print "self.upgrade_type=",self.upgrade_type print "self.set_install_packages=",self.set_install_packages print "self.simulate=", self.simulate return "" class GetterArgs(): def __init__(self, filename=None, bundle_file=None, socket_timeout=30, \ num_of_threads=1, disable_md5check=True, deb_bugs=False, download_dir=None, cache_dir=None, proxy_host=None, proxy_port=None, progress_bar=None, progress_label=None): self.get = filename # TODO: to be implemented in next revision self.socket_timeout = socket_timeout self.num_of_threads = num_of_threads self.bundle_file = bundle_file self.disable_md5check = disable_md5check self.deb_bugs = deb_bugs self.download_dir = download_dir self.cache_dir = cache_dir self.proxy_host = proxy_host self.proxy_port = proxy_port self.progress_bar = progress_bar self.progress_label = progress_label def __str__(self): print "self.get=",self.get print "self.filename=",self.filename print "self.bundle_file=",self.bundle_file print "self.socket_timeout=",self.socket_timeout print "self.num_of_threads=",self.num_of_threads print "self.disable_md5_check=",self.disable_md5check print "self.deb_bugs=",self.deb_bugs print "self.download_dir=",self.download_dir print "self.cache_dir=",self.cache_dir return "" ''' # install opts Str_InstallArg = args.install Bool_TestWindows = args.simulate Bool_SkipBugReports = args.skip_bug_reports Bool_Untrusted = args.allow_unauthenticated Str_InstallSrcPath = args.install_src_path ''' class InstallerArgs(): def __init__(self, filename=None, skip_bug_reports=True, allow_unauthenticated=False, install_src_path=None, progress_bar=None, progress_label=None, simulate = False): self.install = filename # TODO: to be implemented in next revision self.simulate = simulate self.skip_bug_reports = skip_bug_reports self.allow_unauthenticated = allow_unauthenticated self.install_src_path = install_src_path self.progress_bar = progress_bar self.progress_label = progress_label apt-offline-1.3.1/apt_offline_gui/Ui_AptOfflineQtCreateProfile.py0000644000000000000000000002741012125763750021764 0ustar # -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'AptOfflineQtCreateProfile.ui' # # Created: Mon Dec 31 16:02:36 2012 # by: PyQt4 UI code generator 4.9.3 # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui try: _fromUtf8 = QtCore.QString.fromUtf8 except AttributeError: _fromUtf8 = lambda s: s class Ui_CreateProfile(object): def setupUi(self, CreateProfile): CreateProfile.setObjectName(_fromUtf8("CreateProfile")) CreateProfile.setWindowModality(QtCore.Qt.ApplicationModal) CreateProfile.resize(443, 374) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(CreateProfile.sizePolicy().hasHeightForWidth()) CreateProfile.setSizePolicy(sizePolicy) CreateProfile.setMinimumSize(QtCore.QSize(443, 374)) CreateProfile.setMaximumSize(QtCore.QSize(443, 374)) self.verticalLayoutWidget = QtGui.QWidget(CreateProfile) self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 10, 427, 321)) self.verticalLayoutWidget.setObjectName(_fromUtf8("verticalLayoutWidget")) self.verticalLayout = QtGui.QVBoxLayout(self.verticalLayoutWidget) self.verticalLayout.setMargin(0) self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) self.verticalLayout_2 = QtGui.QVBoxLayout() self.verticalLayout_2.setSizeConstraint(QtGui.QLayout.SetFixedSize) self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2")) self.horizontalLayout = QtGui.QHBoxLayout() self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout")) self.verticalLayout_3 = QtGui.QVBoxLayout() self.verticalLayout_3.setSpacing(6) self.verticalLayout_3.setSizeConstraint(QtGui.QLayout.SetMinimumSize) self.verticalLayout_3.setObjectName(_fromUtf8("verticalLayout_3")) self.lblInstallType = QtGui.QLabel(self.verticalLayoutWidget) self.lblInstallType.setMaximumSize(QtCore.QSize(16777215, 16777215)) self.lblInstallType.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) self.lblInstallType.setObjectName(_fromUtf8("lblInstallType")) self.verticalLayout_3.addWidget(self.lblInstallType) self.horizontalLayout.addLayout(self.verticalLayout_3) self.verticalLayout_4 = QtGui.QVBoxLayout() self.verticalLayout_4.setObjectName(_fromUtf8("verticalLayout_4")) self.updateCheckBox = QtGui.QCheckBox(self.verticalLayoutWidget) self.updateCheckBox.setObjectName(_fromUtf8("updateCheckBox")) self.verticalLayout_4.addWidget(self.updateCheckBox) self.upgradePackagesCheckBox = QtGui.QCheckBox(self.verticalLayoutWidget) self.upgradePackagesCheckBox.setObjectName(_fromUtf8("upgradePackagesCheckBox")) self.verticalLayout_4.addWidget(self.upgradePackagesCheckBox) self.installPackagesCheckBox = QtGui.QCheckBox(self.verticalLayoutWidget) self.installPackagesCheckBox.setObjectName(_fromUtf8("installPackagesCheckBox")) self.verticalLayout_4.addWidget(self.installPackagesCheckBox) self.horizontalLayout.addLayout(self.verticalLayout_4) spacerItem = QtGui.QSpacerItem(20, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem) self.verticalLayout_2.addLayout(self.horizontalLayout) self.verticalLayout.addLayout(self.verticalLayout_2) spacerItem1 = QtGui.QSpacerItem(20, 4, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) self.verticalLayout.addItem(spacerItem1) self.horizontalLayout_6 = QtGui.QHBoxLayout() self.horizontalLayout_6.setSizeConstraint(QtGui.QLayout.SetDefaultConstraint) self.horizontalLayout_6.setObjectName(_fromUtf8("horizontalLayout_6")) self.lblPackageList = QtGui.QLabel(self.verticalLayoutWidget) self.lblPackageList.setObjectName(_fromUtf8("lblPackageList")) self.horizontalLayout_6.addWidget(self.lblPackageList) self.packageList = QtGui.QLineEdit(self.verticalLayoutWidget) self.packageList.setEnabled(False) self.packageList.setMinimumSize(QtCore.QSize(0, 30)) self.packageList.setReadOnly(False) self.packageList.setObjectName(_fromUtf8("packageList")) self.horizontalLayout_6.addWidget(self.packageList) self.verticalLayout.addLayout(self.horizontalLayout_6) spacerItem2 = QtGui.QSpacerItem(20, 4, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) self.verticalLayout.addItem(spacerItem2) self.verticalLayout_5 = QtGui.QVBoxLayout() self.verticalLayout_5.setSpacing(6) self.verticalLayout_5.setSizeConstraint(QtGui.QLayout.SetDefaultConstraint) self.verticalLayout_5.setObjectName(_fromUtf8("verticalLayout_5")) self.horizontalLayout_2 = QtGui.QHBoxLayout() self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2")) self.horizontalLayout_3 = QtGui.QHBoxLayout() self.horizontalLayout_3.setObjectName(_fromUtf8("horizontalLayout_3")) self.lblSaveProfile = QtGui.QLabel(self.verticalLayoutWidget) self.lblSaveProfile.setObjectName(_fromUtf8("lblSaveProfile")) self.horizontalLayout_3.addWidget(self.lblSaveProfile) self.profileFilePath = QtGui.QLineEdit(self.verticalLayoutWidget) self.profileFilePath.setEnabled(True) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.profileFilePath.sizePolicy().hasHeightForWidth()) self.profileFilePath.setSizePolicy(sizePolicy) self.profileFilePath.setMinimumSize(QtCore.QSize(0, 30)) self.profileFilePath.setObjectName(_fromUtf8("profileFilePath")) self.horizontalLayout_3.addWidget(self.profileFilePath) self.browseFilePathButton = QtGui.QPushButton(self.verticalLayoutWidget) self.browseFilePathButton.setMinimumSize(QtCore.QSize(0, 30)) self.browseFilePathButton.setObjectName(_fromUtf8("browseFilePathButton")) self.horizontalLayout_3.addWidget(self.browseFilePathButton) self.horizontalLayout_2.addLayout(self.horizontalLayout_3) self.verticalLayout_5.addLayout(self.horizontalLayout_2) self.verticalLayout_8 = QtGui.QVBoxLayout() self.verticalLayout_8.setObjectName(_fromUtf8("verticalLayout_8")) self.lblConsoleOutput = QtGui.QLabel(self.verticalLayoutWidget) self.lblConsoleOutput.setObjectName(_fromUtf8("lblConsoleOutput")) self.verticalLayout_8.addWidget(self.lblConsoleOutput) self.consoleOutputHolder = QtGui.QTextEdit(self.verticalLayoutWidget) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.consoleOutputHolder.sizePolicy().hasHeightForWidth()) self.consoleOutputHolder.setSizePolicy(sizePolicy) self.consoleOutputHolder.setMinimumSize(QtCore.QSize(0, 100)) self.consoleOutputHolder.setMaximumSize(QtCore.QSize(16777215, 100)) self.consoleOutputHolder.setObjectName(_fromUtf8("consoleOutputHolder")) self.verticalLayout_8.addWidget(self.consoleOutputHolder) self.verticalLayout_5.addLayout(self.verticalLayout_8) self.verticalLayout.addLayout(self.verticalLayout_5) self.horizontalLayoutWidget = QtGui.QWidget(CreateProfile) self.horizontalLayoutWidget.setGeometry(QtCore.QRect(25, 325, 401, 40)) self.horizontalLayoutWidget.setObjectName(_fromUtf8("horizontalLayoutWidget")) self.horizontalLayout_4 = QtGui.QHBoxLayout(self.horizontalLayoutWidget) self.horizontalLayout_4.setMargin(0) self.horizontalLayout_4.setObjectName(_fromUtf8("horizontalLayout_4")) spacerItem3 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) self.horizontalLayout_4.addItem(spacerItem3) self.createProfileButton = QtGui.QPushButton(self.horizontalLayoutWidget) self.createProfileButton.setEnabled(True) self.createProfileButton.setMinimumSize(QtCore.QSize(100, 30)) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/icons/icons/dialog-ok-apply.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.createProfileButton.setIcon(icon) self.createProfileButton.setObjectName(_fromUtf8("createProfileButton")) self.horizontalLayout_4.addWidget(self.createProfileButton) spacerItem4 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) self.horizontalLayout_4.addItem(spacerItem4) self.cancelButton = QtGui.QPushButton(self.horizontalLayoutWidget) self.cancelButton.setMinimumSize(QtCore.QSize(100, 30)) icon1 = QtGui.QIcon() icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/icons/icons/dialog-cancel.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.cancelButton.setIcon(icon1) self.cancelButton.setObjectName(_fromUtf8("cancelButton")) self.horizontalLayout_4.addWidget(self.cancelButton) spacerItem5 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) self.horizontalLayout_4.addItem(spacerItem5) self.retranslateUi(CreateProfile) QtCore.QMetaObject.connectSlotsByName(CreateProfile) def retranslateUi(self, CreateProfile): CreateProfile.setWindowTitle(QtGui.QApplication.translate("CreateProfile", "Generate Signature", None, QtGui.QApplication.UnicodeUTF8)) self.lblInstallType.setText(QtGui.QApplication.translate("CreateProfile", "Installation Type", None, QtGui.QApplication.UnicodeUTF8)) self.updateCheckBox.setText(QtGui.QApplication.translate("CreateProfile", "Update", None, QtGui.QApplication.UnicodeUTF8)) self.upgradePackagesCheckBox.setText(QtGui.QApplication.translate("CreateProfile", "Upgrade Packages", None, QtGui.QApplication.UnicodeUTF8)) self.installPackagesCheckBox.setText(QtGui.QApplication.translate("CreateProfile", "Install Packages", None, QtGui.QApplication.UnicodeUTF8)) self.lblPackageList.setText(QtGui.QApplication.translate("CreateProfile", "\n" "\n" "

    Install these packages

    \n" "

    separate by comma

    ", None, QtGui.QApplication.UnicodeUTF8)) self.lblSaveProfile.setText(QtGui.QApplication.translate("CreateProfile", "Save Signature As ", None, QtGui.QApplication.UnicodeUTF8)) self.browseFilePathButton.setText(QtGui.QApplication.translate("CreateProfile", "Browse", None, QtGui.QApplication.UnicodeUTF8)) self.lblConsoleOutput.setText(QtGui.QApplication.translate("CreateProfile", "Console Output:", None, QtGui.QApplication.UnicodeUTF8)) self.createProfileButton.setText(QtGui.QApplication.translate("CreateProfile", "Create", None, QtGui.QApplication.UnicodeUTF8)) self.cancelButton.setText(QtGui.QApplication.translate("CreateProfile", "Cancel", None, QtGui.QApplication.UnicodeUTF8)) import resources_rc apt-offline-1.3.1/apt_offline_gui/Ui_AptOfflineQtFetch.py0000644000000000000000000001675112125763750020277 0ustar # -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'AptOfflineQtFetch.ui' # # Created: Mon Dec 31 16:02:36 2012 # by: PyQt4 UI code generator 4.9.3 # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui try: _fromUtf8 = QtCore.QString.fromUtf8 except AttributeError: _fromUtf8 = lambda s: s class Ui_AptOfflineQtFetch(object): def setupUi(self, AptOfflineQtFetch): AptOfflineQtFetch.setObjectName(_fromUtf8("AptOfflineQtFetch")) AptOfflineQtFetch.setWindowModality(QtCore.Qt.ApplicationModal) AptOfflineQtFetch.resize(468, 483) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(AptOfflineQtFetch.sizePolicy().hasHeightForWidth()) AptOfflineQtFetch.setSizePolicy(sizePolicy) AptOfflineQtFetch.setMinimumSize(QtCore.QSize(468, 475)) AptOfflineQtFetch.setMaximumSize(QtCore.QSize(468, 495)) self.profileFilePath = QtGui.QLineEdit(AptOfflineQtFetch) self.profileFilePath.setGeometry(QtCore.QRect(30, 35, 270, 30)) self.profileFilePath.setObjectName(_fromUtf8("profileFilePath")) self.browseFilePathButton = QtGui.QPushButton(AptOfflineQtFetch) self.browseFilePathButton.setGeometry(QtCore.QRect(320, 35, 110, 30)) self.browseFilePathButton.setObjectName(_fromUtf8("browseFilePathButton")) self.startDownloadButton = QtGui.QPushButton(AptOfflineQtFetch) self.startDownloadButton.setEnabled(False) self.startDownloadButton.setGeometry(QtCore.QRect(179, 148, 120, 30)) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/icons/icons/go-down.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.startDownloadButton.setIcon(icon) self.startDownloadButton.setShortcut(_fromUtf8("")) self.startDownloadButton.setCheckable(False) self.startDownloadButton.setChecked(False) self.startDownloadButton.setFlat(False) self.startDownloadButton.setObjectName(_fromUtf8("startDownloadButton")) self.cancelButton = QtGui.QPushButton(AptOfflineQtFetch) self.cancelButton.setGeometry(QtCore.QRect(319, 148, 111, 30)) icon1 = QtGui.QIcon() icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/icons/icons/application-exit.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.cancelButton.setIcon(icon1) self.cancelButton.setObjectName(_fromUtf8("cancelButton")) self.lblSelectSig = QtGui.QLabel(AptOfflineQtFetch) self.lblSelectSig.setGeometry(QtCore.QRect(30, 5, 200, 30)) font = QtGui.QFont() font.setPointSize(10) self.lblSelectSig.setFont(font) self.lblSelectSig.setObjectName(_fromUtf8("lblSelectSig")) self.statusProgressBar = QtGui.QProgressBar(AptOfflineQtFetch) self.statusProgressBar.setGeometry(QtCore.QRect(30, 214, 410, 20)) self.statusProgressBar.setProperty("value", 0) self.statusProgressBar.setObjectName(_fromUtf8("statusProgressBar")) self.lblStatus = QtGui.QLabel(AptOfflineQtFetch) self.lblStatus.setGeometry(QtCore.QRect(32, 194, 70, 16)) font = QtGui.QFont() font.setPointSize(10) self.lblStatus.setFont(font) self.lblStatus.setObjectName(_fromUtf8("lblStatus")) self.progressStatusDescription = QtGui.QLabel(AptOfflineQtFetch) self.progressStatusDescription.setGeometry(QtCore.QRect(82, 194, 341, 16)) font = QtGui.QFont() font.setPointSize(10) self.progressStatusDescription.setFont(font) self.progressStatusDescription.setObjectName(_fromUtf8("progressStatusDescription")) self.rawLogHolder = QtGui.QTextEdit(AptOfflineQtFetch) self.rawLogHolder.setGeometry(QtCore.QRect(30, 252, 411, 211)) self.rawLogHolder.setAcceptDrops(False) self.rawLogHolder.setReadOnly(True) self.rawLogHolder.setObjectName(_fromUtf8("rawLogHolder")) self.browseZipFileButton = QtGui.QPushButton(AptOfflineQtFetch) self.browseZipFileButton.setGeometry(QtCore.QRect(320, 98, 110, 30)) self.browseZipFileButton.setObjectName(_fromUtf8("browseZipFileButton")) self.lblSaveAs = QtGui.QLabel(AptOfflineQtFetch) self.lblSaveAs.setGeometry(QtCore.QRect(30, 68, 200, 30)) font = QtGui.QFont() font.setPointSize(10) self.lblSaveAs.setFont(font) self.lblSaveAs.setObjectName(_fromUtf8("lblSaveAs")) self.zipFilePath = QtGui.QLineEdit(AptOfflineQtFetch) self.zipFilePath.setGeometry(QtCore.QRect(30, 98, 270, 30)) self.zipFilePath.setObjectName(_fromUtf8("zipFilePath")) self.advancedOptionsButton = QtGui.QPushButton(AptOfflineQtFetch) self.advancedOptionsButton.setEnabled(True) self.advancedOptionsButton.setGeometry(QtCore.QRect(31, 148, 131, 30)) icon2 = QtGui.QIcon() icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/icons/icons/configure.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.advancedOptionsButton.setIcon(icon2) self.advancedOptionsButton.setShortcut(_fromUtf8("")) self.advancedOptionsButton.setCheckable(False) self.advancedOptionsButton.setChecked(False) self.advancedOptionsButton.setFlat(False) self.advancedOptionsButton.setObjectName(_fromUtf8("advancedOptionsButton")) self.saveDatacheckBox = QtGui.QCheckBox(AptOfflineQtFetch) self.saveDatacheckBox.setGeometry(QtCore.QRect(321, 80, 110, 18)) self.saveDatacheckBox.setObjectName(_fromUtf8("saveDatacheckBox")) self.retranslateUi(AptOfflineQtFetch) QtCore.QMetaObject.connectSlotsByName(AptOfflineQtFetch) def retranslateUi(self, AptOfflineQtFetch): AptOfflineQtFetch.setWindowTitle(QtGui.QApplication.translate("AptOfflineQtFetch", "Fetch Packages or Updates", None, QtGui.QApplication.UnicodeUTF8)) self.browseFilePathButton.setText(QtGui.QApplication.translate("AptOfflineQtFetch", "Browse", None, QtGui.QApplication.UnicodeUTF8)) self.startDownloadButton.setText(QtGui.QApplication.translate("AptOfflineQtFetch", "Download", None, QtGui.QApplication.UnicodeUTF8)) self.cancelButton.setText(QtGui.QApplication.translate("AptOfflineQtFetch", "Close", None, QtGui.QApplication.UnicodeUTF8)) self.lblSelectSig.setText(QtGui.QApplication.translate("AptOfflineQtFetch", "Select the signature file", None, QtGui.QApplication.UnicodeUTF8)) self.lblStatus.setText(QtGui.QApplication.translate("AptOfflineQtFetch", "Status:", None, QtGui.QApplication.UnicodeUTF8)) self.progressStatusDescription.setText(QtGui.QApplication.translate("AptOfflineQtFetch", "Ready", None, QtGui.QApplication.UnicodeUTF8)) self.browseZipFileButton.setText(QtGui.QApplication.translate("AptOfflineQtFetch", "Browse", None, QtGui.QApplication.UnicodeUTF8)) self.lblSaveAs.setText(QtGui.QApplication.translate("AptOfflineQtFetch", "Save data as", None, QtGui.QApplication.UnicodeUTF8)) self.advancedOptionsButton.setToolTip(QtGui.QApplication.translate("AptOfflineQtFetch", "Additional options for download", None, QtGui.QApplication.UnicodeUTF8)) self.advancedOptionsButton.setText(QtGui.QApplication.translate("AptOfflineQtFetch", "Options", None, QtGui.QApplication.UnicodeUTF8)) self.saveDatacheckBox.setText(QtGui.QApplication.translate("AptOfflineQtFetch", "Is Directory", None, QtGui.QApplication.UnicodeUTF8)) import resources_rc apt-offline-1.3.1/apt_offline_gui/AptOfflineQtSaveZip.py0000644000000000000000000000257011465541222020156 0ustar import sys from PyQt4 import QtCore, QtGui from apt_offline_gui.Ui_AptOfflineQtSaveZip import Ui_SaveZipFile class AptOfflineQtSaveZip(QtGui.QDialog): def __init__(self, parent=None): QtGui.QWidget.__init__(self, parent) self.ui = Ui_SaveZipFile() self.ui.setupUi(self) # Connect the clicked signal of the Browse button to it's slot QtCore.QObject.connect(self.ui.browseFilePathButton, QtCore.SIGNAL("clicked()"), self.popupDirectoryDialog ) # Connect the clicked signal of the Save to it's Slot - accept QtCore.QObject.connect(self.ui.saveButton, QtCore.SIGNAL("clicked()"), self.accept ) # Connect the clicked signal of the Cancel to it's Slot - reject QtCore.QObject.connect(self.ui.cancelButton, QtCore.SIGNAL("clicked()"), self.reject ) def popupDirectoryDialog(self): # Popup a Directory selection box directory = QtGui.QFileDialog.getExistingDirectory(self, u'Open Directory') # Show the selected file path in the field marked for showing directory path self.ui.zipFilePath.setText(directory) if __name__ == "__main__": app = QtGui.QApplication(sys.argv) myapp = AptOfflineQtSaveZip() myapp.show() sys.exit(app.exec_()) apt-offline-1.3.1/apt_offline_gui/Ui_AptOfflineQtAbout.py0000644000000000000000000002302112125763750020304 0ustar # -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'AptOfflineQtAbout.ui' # # Created: Mon Dec 31 16:02:37 2012 # by: PyQt4 UI code generator 4.9.3 # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui try: _fromUtf8 = QtCore.QString.fromUtf8 except AttributeError: _fromUtf8 = lambda s: s class Ui_AboutAptOffline(object): def setupUi(self, AboutAptOffline): AboutAptOffline.setObjectName(_fromUtf8("AboutAptOffline")) AboutAptOffline.setWindowModality(QtCore.Qt.ApplicationModal) AboutAptOffline.resize(526, 378) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(AboutAptOffline.sizePolicy().hasHeightForWidth()) AboutAptOffline.setSizePolicy(sizePolicy) AboutAptOffline.setMinimumSize(QtCore.QSize(526, 378)) AboutAptOffline.setMaximumSize(QtCore.QSize(526, 378)) self.label = QtGui.QLabel(AboutAptOffline) self.label.setGeometry(QtCore.QRect(12, 30, 511, 21)) font = QtGui.QFont() font.setPointSize(16) self.label.setFont(font) self.label.setAlignment(QtCore.Qt.AlignCenter) self.label.setObjectName(_fromUtf8("label")) self.tabWidget = QtGui.QTabWidget(AboutAptOffline) self.tabWidget.setGeometry(QtCore.QRect(7, 90, 510, 241)) self.tabWidget.setObjectName(_fromUtf8("tabWidget")) self.aboutTab = QtGui.QWidget() self.aboutTab.setObjectName(_fromUtf8("aboutTab")) self.label_3 = QtGui.QLabel(self.aboutTab) self.label_3.setGeometry(QtCore.QRect(10, 20, 491, 31)) self.label_3.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) self.label_3.setWordWrap(True) self.label_3.setObjectName(_fromUtf8("label_3")) self.label_14 = QtGui.QLabel(self.aboutTab) self.label_14.setGeometry(QtCore.QRect(10, 60, 481, 111)) self.label_14.setScaledContents(True) self.label_14.setWordWrap(True) self.label_14.setObjectName(_fromUtf8("label_14")) self.tabWidget.addTab(self.aboutTab, _fromUtf8("")) self.authorTab = QtGui.QWidget() self.authorTab.setObjectName(_fromUtf8("authorTab")) self.label_4 = QtGui.QLabel(self.authorTab) self.label_4.setGeometry(QtCore.QRect(10, 10, 111, 16)) self.label_4.setObjectName(_fromUtf8("label_4")) self.label_5 = QtGui.QLabel(self.authorTab) self.label_5.setGeometry(QtCore.QRect(30, 30, 271, 16)) self.label_5.setObjectName(_fromUtf8("label_5")) self.label_6 = QtGui.QLabel(self.authorTab) self.label_6.setGeometry(QtCore.QRect(10, 60, 131, 16)) self.label_6.setObjectName(_fromUtf8("label_6")) self.label_7 = QtGui.QLabel(self.authorTab) self.label_7.setGeometry(QtCore.QRect(30, 80, 261, 16)) self.label_7.setObjectName(_fromUtf8("label_7")) self.label_8 = QtGui.QLabel(self.authorTab) self.label_8.setGeometry(QtCore.QRect(30, 100, 271, 16)) self.label_8.setObjectName(_fromUtf8("label_8")) self.tabWidget.addTab(self.authorTab, _fromUtf8("")) self.thanksTab = QtGui.QWidget() self.thanksTab.setObjectName(_fromUtf8("thanksTab")) self.label_9 = QtGui.QLabel(self.thanksTab) self.label_9.setGeometry(QtCore.QRect(10, 10, 221, 16)) self.label_9.setObjectName(_fromUtf8("label_9")) self.label_10 = QtGui.QLabel(self.thanksTab) self.label_10.setGeometry(QtCore.QRect(10, 30, 141, 16)) self.label_10.setObjectName(_fromUtf8("label_10")) self.label_11 = QtGui.QLabel(self.thanksTab) self.label_11.setGeometry(QtCore.QRect(10, 50, 161, 16)) self.label_11.setObjectName(_fromUtf8("label_11")) self.label_12 = QtGui.QLabel(self.thanksTab) self.label_12.setGeometry(QtCore.QRect(10, 70, 161, 16)) self.label_12.setObjectName(_fromUtf8("label_12")) self.label_13 = QtGui.QLabel(self.thanksTab) self.label_13.setGeometry(QtCore.QRect(10, 110, 471, 51)) self.label_13.setScaledContents(True) self.label_13.setWordWrap(True) self.label_13.setObjectName(_fromUtf8("label_13")) self.tabWidget.addTab(self.thanksTab, _fromUtf8("")) self.licenseTab = QtGui.QWidget() self.licenseTab.setObjectName(_fromUtf8("licenseTab")) self.licenseText = QtGui.QPlainTextEdit(self.licenseTab) self.licenseText.setGeometry(QtCore.QRect(4, 4, 490, 203)) font = QtGui.QFont() font.setPointSize(8) self.licenseText.setFont(font) self.licenseText.setAcceptDrops(False) self.licenseText.setUndoRedoEnabled(False) self.licenseText.setReadOnly(True) self.licenseText.setObjectName(_fromUtf8("licenseText")) self.tabWidget.addTab(self.licenseTab, _fromUtf8("")) self.label_2 = QtGui.QLabel(AboutAptOffline) self.label_2.setGeometry(QtCore.QRect(10, 60, 511, 16)) self.label_2.setAlignment(QtCore.Qt.AlignCenter) self.label_2.setObjectName(_fromUtf8("label_2")) self.pushButton = QtGui.QPushButton(AboutAptOffline) self.pushButton.setGeometry(QtCore.QRect(416, 340, 101, 31)) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/icons/icons/dialog-cancel.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.pushButton.setIcon(icon) self.pushButton.setObjectName(_fromUtf8("pushButton")) self.retranslateUi(AboutAptOffline) self.tabWidget.setCurrentIndex(3) QtCore.QObject.connect(self.pushButton, QtCore.SIGNAL(_fromUtf8("clicked()")), AboutAptOffline.close) QtCore.QMetaObject.connectSlotsByName(AboutAptOffline) def retranslateUi(self, AboutAptOffline): AboutAptOffline.setWindowTitle(QtGui.QApplication.translate("AboutAptOffline", "About Apt-Offline", None, QtGui.QApplication.UnicodeUTF8)) self.label.setText(QtGui.QApplication.translate("AboutAptOffline", "Apt-Offline", None, QtGui.QApplication.UnicodeUTF8)) self.label_3.setText(QtGui.QApplication.translate("AboutAptOffline", "apt-offline is an Offline APT Package Manager for Debian and derivatives. ", None, QtGui.QApplication.UnicodeUTF8)) self.label_14.setText(QtGui.QApplication.translate("AboutAptOffline", "apt-offline can fully update/upgrade your disconnected Debian box without the need of connecting it to the network. \n" "\n" "This is a Graphical User Interface which exposes the functionality of apt-offline.", None, QtGui.QApplication.UnicodeUTF8)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.aboutTab), QtGui.QApplication.translate("AboutAptOffline", "About", None, QtGui.QApplication.UnicodeUTF8)) self.label_4.setText(QtGui.QApplication.translate("AboutAptOffline", "Written by:", None, QtGui.QApplication.UnicodeUTF8)) self.label_5.setText(QtGui.QApplication.translate("AboutAptOffline", "Ritesh Raj Sarraf ", None, QtGui.QApplication.UnicodeUTF8)) self.label_6.setText(QtGui.QApplication.translate("AboutAptOffline", "GUI written by:", None, QtGui.QApplication.UnicodeUTF8)) self.label_7.setText(QtGui.QApplication.translate("AboutAptOffline", "Manish Sinha ", None, QtGui.QApplication.UnicodeUTF8)) self.label_8.setText(QtGui.QApplication.translate("AboutAptOffline", "Abhishek Mishra ", None, QtGui.QApplication.UnicodeUTF8)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.authorTab), QtGui.QApplication.translate("AboutAptOffline", "Author", None, QtGui.QApplication.UnicodeUTF8)) self.label_9.setText(QtGui.QApplication.translate("AboutAptOffline", "Peter Otten", None, QtGui.QApplication.UnicodeUTF8)) self.label_10.setText(QtGui.QApplication.translate("AboutAptOffline", "Duncan Booth", None, QtGui.QApplication.UnicodeUTF8)) self.label_11.setText(QtGui.QApplication.translate("AboutAptOffline", "Simon Forman", None, QtGui.QApplication.UnicodeUTF8)) self.label_12.setText(QtGui.QApplication.translate("AboutAptOffline", "Dennis Lee Bieber", None, QtGui.QApplication.UnicodeUTF8)) self.label_13.setText(QtGui.QApplication.translate("AboutAptOffline", "The awesome Directi people for their office space required for the mini hackfests", None, QtGui.QApplication.UnicodeUTF8)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.thanksTab), QtGui.QApplication.translate("AboutAptOffline", "Thanks To", None, QtGui.QApplication.UnicodeUTF8)) self.licenseText.setPlainText(QtGui.QApplication.translate("AboutAptOffline", " GNU GENERAL PUBLIC LICENSE\n" " Version 3, 29 June 2007\n" "\n" " Copyright (C) 2007 Free Software Foundation, Inc. \n" " Everyone is permitted to copy and distribute verbatim copies\n" " of this license document, but changing it is not allowed.\n" "\n" "\n" "apt-offline is Copyright (C) - Ritesh Raj Sarraf", None, QtGui.QApplication.UnicodeUTF8)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.licenseTab), QtGui.QApplication.translate("AboutAptOffline", "License", None, QtGui.QApplication.UnicodeUTF8)) self.label_2.setText(QtGui.QApplication.translate("AboutAptOffline", "A GUI for apt-offline - an offline APT Package Manager", None, QtGui.QApplication.UnicodeUTF8)) self.pushButton.setText(QtGui.QApplication.translate("AboutAptOffline", "Close", None, QtGui.QApplication.UnicodeUTF8)) import resources_rc apt-offline-1.3.1/apt_offline_gui/AptOfflineQtInstallBugList.ui0000644000000000000000000000422112125763750021463 0ustar AptOfflineQtInstallBugList 0 0 642 674 0 0 List of Bugs true 30 40 581 81 500 630 107 24 <html><head/><body><p>Close this window</p></body></html> Close :/icons/icons/application-exit.png:/icons/icons/application-exit.png 30 10 231 16 List of bugs 30 160 581 441 false true apt-offline-1.3.1/apt_offline_gui/Ui_AptOfflineQtMain.py0000644000000000000000000002320712125763750020124 0ustar # -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'AptOfflineQtMain.ui' # # Created: Mon Dec 31 16:02:36 2012 # by: PyQt4 UI code generator 4.9.3 # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui try: _fromUtf8 = QtCore.QString.fromUtf8 except AttributeError: _fromUtf8 = lambda s: s class Ui_AptOfflineMain(object): def setupUi(self, AptOfflineMain): AptOfflineMain.setObjectName(_fromUtf8("AptOfflineMain")) AptOfflineMain.resize(432, 544) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(AptOfflineMain.sizePolicy().hasHeightForWidth()) AptOfflineMain.setSizePolicy(sizePolicy) AptOfflineMain.setMinimumSize(QtCore.QSize(432, 544)) AptOfflineMain.setMaximumSize(QtCore.QSize(432, 544)) AptOfflineMain.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly) AptOfflineMain.setUnifiedTitleAndToolBarOnMac(True) self.centralwidget = QtGui.QWidget(AptOfflineMain) self.centralwidget.setObjectName(_fromUtf8("centralwidget")) self.createProfileButton = QtGui.QPushButton(self.centralwidget) self.createProfileButton.setGeometry(QtCore.QRect(30, 20, 371, 40)) font = QtGui.QFont() font.setPointSize(12) self.createProfileButton.setFont(font) self.createProfileButton.setMouseTracking(True) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/icons/icons/contact-new.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.createProfileButton.setIcon(icon) self.createProfileButton.setObjectName(_fromUtf8("createProfileButton")) self.downloadButton = QtGui.QPushButton(self.centralwidget) self.downloadButton.setGeometry(QtCore.QRect(30, 80, 371, 41)) font = QtGui.QFont() font.setPointSize(12) font.setBold(False) font.setWeight(50) self.downloadButton.setFont(font) icon1 = QtGui.QIcon() icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/icons/icons/go-down.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.downloadButton.setIcon(icon1) self.downloadButton.setObjectName(_fromUtf8("downloadButton")) self.restoreButton = QtGui.QPushButton(self.centralwidget) self.restoreButton.setGeometry(QtCore.QRect(30, 140, 371, 41)) font = QtGui.QFont() font.setPointSize(12) self.restoreButton.setFont(font) icon2 = QtGui.QIcon() icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/icons/icons/install.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.restoreButton.setIcon(icon2) self.restoreButton.setObjectName(_fromUtf8("restoreButton")) self.frame = QtGui.QFrame(self.centralwidget) self.frame.setGeometry(QtCore.QRect(30, 220, 371, 211)) font = QtGui.QFont() font.setPointSize(10) self.frame.setFont(font) self.frame.setFrameShape(QtGui.QFrame.Box) self.frame.setFrameShadow(QtGui.QFrame.Plain) self.frame.setLineWidth(1) self.frame.setObjectName(_fromUtf8("frame")) self.descriptionField = QtGui.QLabel(self.frame) self.descriptionField.setGeometry(QtCore.QRect(0, 0, 371, 211)) self.descriptionField.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) self.descriptionField.setWordWrap(True) self.descriptionField.setMargin(10) self.descriptionField.setObjectName(_fromUtf8("descriptionField")) self.exitButton = QtGui.QPushButton(self.centralwidget) self.exitButton.setGeometry(QtCore.QRect(280, 450, 121, 41)) font = QtGui.QFont() font.setPointSize(10) self.exitButton.setFont(font) icon3 = QtGui.QIcon() icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/icons/icons/application-exit.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.exitButton.setIcon(icon3) self.exitButton.setObjectName(_fromUtf8("exitButton")) self.label_2 = QtGui.QLabel(self.centralwidget) self.label_2.setGeometry(QtCore.QRect(30, 200, 91, 21)) font = QtGui.QFont() font.setPointSize(10) self.label_2.setFont(font) self.label_2.setObjectName(_fromUtf8("label_2")) AptOfflineMain.setCentralWidget(self.centralwidget) self.statusbar = QtGui.QStatusBar(AptOfflineMain) self.statusbar.setObjectName(_fromUtf8("statusbar")) AptOfflineMain.setStatusBar(self.statusbar) self.menubar = QtGui.QMenuBar(AptOfflineMain) self.menubar.setGeometry(QtCore.QRect(0, 0, 432, 25)) self.menubar.setObjectName(_fromUtf8("menubar")) self.menuOperations = QtGui.QMenu(self.menubar) self.menuOperations.setObjectName(_fromUtf8("menuOperations")) self.menuHelp = QtGui.QMenu(self.menubar) self.menuHelp.setObjectName(_fromUtf8("menuHelp")) AptOfflineMain.setMenuBar(self.menubar) self.menuCreateProfile = QtGui.QAction(AptOfflineMain) self.menuCreateProfile.setIcon(icon) self.menuCreateProfile.setObjectName(_fromUtf8("menuCreateProfile")) self.menuDownload = QtGui.QAction(AptOfflineMain) self.menuDownload.setIcon(icon1) self.menuDownload.setObjectName(_fromUtf8("menuDownload")) self.menuInstall = QtGui.QAction(AptOfflineMain) self.menuInstall.setIcon(icon2) self.menuInstall.setObjectName(_fromUtf8("menuInstall")) self.menuExit = QtGui.QAction(AptOfflineMain) self.menuExit.setIcon(icon3) self.menuExit.setObjectName(_fromUtf8("menuExit")) self.menuHelp_2 = QtGui.QAction(AptOfflineMain) icon4 = QtGui.QIcon() icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/icons/icons/help-contents.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.menuHelp_2.setIcon(icon4) self.menuHelp_2.setObjectName(_fromUtf8("menuHelp_2")) self.menuAbout = QtGui.QAction(AptOfflineMain) icon5 = QtGui.QIcon() icon5.addPixmap(QtGui.QPixmap(_fromUtf8(":/icons/icons/help-about.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.menuAbout.setIcon(icon5) self.menuAbout.setObjectName(_fromUtf8("menuAbout")) self.menuOperations.addAction(self.menuCreateProfile) self.menuOperations.addAction(self.menuDownload) self.menuOperations.addAction(self.menuInstall) self.menuOperations.addSeparator() self.menuOperations.addAction(self.menuExit) self.menuHelp.addAction(self.menuHelp_2) self.menuHelp.addAction(self.menuAbout) self.menubar.addAction(self.menuOperations.menuAction()) self.menubar.addAction(self.menuHelp.menuAction()) self.retranslateUi(AptOfflineMain) QtCore.QMetaObject.connectSlotsByName(AptOfflineMain) def retranslateUi(self, AptOfflineMain): AptOfflineMain.setWindowTitle(QtGui.QApplication.translate("AptOfflineMain", "APT Offline", None, QtGui.QApplication.UnicodeUTF8)) self.createProfileButton.setText(QtGui.QApplication.translate("AptOfflineMain", "Generate Signature", None, QtGui.QApplication.UnicodeUTF8)) self.downloadButton.setText(QtGui.QApplication.translate("AptOfflineMain", "Download Packages or Updates", None, QtGui.QApplication.UnicodeUTF8)) self.restoreButton.setText(QtGui.QApplication.translate("AptOfflineMain", "Install Packages or Updates", None, QtGui.QApplication.UnicodeUTF8)) self.descriptionField.setText(QtGui.QApplication.translate("AptOfflineMain", "Hover your mouse over the buttons to get the description", None, QtGui.QApplication.UnicodeUTF8)) self.exitButton.setText(QtGui.QApplication.translate("AptOfflineMain", "Exit", None, QtGui.QApplication.UnicodeUTF8)) self.label_2.setText(QtGui.QApplication.translate("AptOfflineMain", "Description", None, QtGui.QApplication.UnicodeUTF8)) self.menuOperations.setTitle(QtGui.QApplication.translate("AptOfflineMain", "Operations", None, QtGui.QApplication.UnicodeUTF8)) self.menuHelp.setTitle(QtGui.QApplication.translate("AptOfflineMain", "Help", None, QtGui.QApplication.UnicodeUTF8)) self.menuCreateProfile.setText(QtGui.QApplication.translate("AptOfflineMain", "Generate Signature", None, QtGui.QApplication.UnicodeUTF8)) self.menuCreateProfile.setShortcut(QtGui.QApplication.translate("AptOfflineMain", "Ctrl+N", None, QtGui.QApplication.UnicodeUTF8)) self.menuDownload.setText(QtGui.QApplication.translate("AptOfflineMain", "Download Packages or Updates", None, QtGui.QApplication.UnicodeUTF8)) self.menuDownload.setShortcut(QtGui.QApplication.translate("AptOfflineMain", "Ctrl+O", None, QtGui.QApplication.UnicodeUTF8)) self.menuInstall.setText(QtGui.QApplication.translate("AptOfflineMain", "Install Packages or Updates", None, QtGui.QApplication.UnicodeUTF8)) self.menuInstall.setShortcut(QtGui.QApplication.translate("AptOfflineMain", "Ctrl+I", None, QtGui.QApplication.UnicodeUTF8)) self.menuExit.setText(QtGui.QApplication.translate("AptOfflineMain", "Exit", None, QtGui.QApplication.UnicodeUTF8)) self.menuExit.setShortcut(QtGui.QApplication.translate("AptOfflineMain", "Ctrl+Q", None, QtGui.QApplication.UnicodeUTF8)) self.menuHelp_2.setText(QtGui.QApplication.translate("AptOfflineMain", "Help", None, QtGui.QApplication.UnicodeUTF8)) self.menuHelp_2.setShortcut(QtGui.QApplication.translate("AptOfflineMain", "F1", None, QtGui.QApplication.UnicodeUTF8)) self.menuAbout.setText(QtGui.QApplication.translate("AptOfflineMain", "About apt-offline", None, QtGui.QApplication.UnicodeUTF8)) import resources_rc apt-offline-1.3.1/apt_offline_gui/__init__.py0000644000000000000000000000000011527757641016056 0ustar apt-offline-1.3.1/apt-offline.html0000644000000000000000000002165212070343010013674 0ustar Content-type: text/html Man page of apt-offline

    apt-offline

    Section: USER COMMANDS (8)
    Updated: April, 2011
    Index Return to Main Contents
     

    NAME

    apt-offline - Offline APT Package manager  

    SYNOPSIS

    apt-offline [ARGUMENTS] [OPTIONS]

     

    DESCRIPTION

    apt-offline brings offline package management functionality to Debian based system. It can be used to download packages and its dependencies to be installed later on (or required to update) a disconnected machine. Packages can be downloaded from a different connected machine.

    It can also fetch bug reports for the packages that are downloaded.

    Unless the -h/-v or --help/--version options are given, one of the get, set or install commands must be provided.

     

    set FILENAME [-h] [--install-packages PKG...PKG_N] [--install-src-packages PKG...PKG_N] --src-build-dep [--release release_name] [--update] [--upgrade] [--upgrade-type upgrade]

    set generates a signature file with the data required to install on or update the disconnected machine.

    FILENAME {apt-offline.sig} is the signature file generated on the machine. This file will contain all the information necessary for apt.

    --install-packages [PKG...PKG_N]
    Packages that need to be installed

    --install-src-packages [PKG...PKG_N]
    Source packages that need to be installed

    --src-build-dep
    Download Build Dependency packages for the source packages requested

    --release release_name
    Release target to install packages from

    --update
    Generate APT Database signature for an update. This is the equivalent of using apt-get update

    --upgrade
    Generate APT Database signature for package upgrade. This is the equivalent of using apt-get upgrade

    --upgrade-type {upgrade_type}
    Type of upgrade you would like to perform. Default is upgrade. Other valid types are dist-upgrade and dselect-upgrade

     

    get FILENAME [-h] [--socket-timeout ] [-d / --download-dir] [-s / --cache-dir] [--no-checksum] [-t / --threads ] [--bundle] [--bug-reports] [--proxy-host] [--proxy-port]

    get downloads APT data as per a signature file.

    FILENAME {apt-offline.sig} is the signature file required for details about data to be downloaded. This file should have been generated by the above set command.

    -d, --download-dir DIR_NAME
    Download data to the specified DIR_NAME folder. If no folder name is specified, data is downloaded to a folder in the TEMPDIR path in the format apt-offline-download-$PID

    -s, --cache-dir DIR_NAME
    Look for data in the cache before downloading it from the internet. If you are on a Debian box, you would want to specify /var/cache/apt/archives here. If the data is not available in the cache, the downloaded data is also copied to the cache.

    --no-checksum
    Enabling this option will bypass the checksum verification of each downloaded file thus losing integrity of the package. Usage of this option is highly discouraged

    -t, --threads NUM_OF_THREADS
    Number of threads to spawn for downloads. Default is 1. Using too many threads can overload the servers, hence it is advisable to keep the number low

    --bundle FILENAME
    Create an archive file FILENAME. The file is archived in zip format

    --bug-reports
    Download bug reports for packages that are being downloaded. Currently only the Debian BTS is supported.

    --proxy-host
    Specify the Proxy Host to be used.

    --proxy-port
    Specify the Proxy port number to be used.

     

    install FILE/FOLDER [-h] [--skip-bug-reports ] [--install-src-path PATH] [--allow-unauthenticated]

    install installs APT data to the APT package database and updates it.

    FILE {archive_bundle_file} Install data from the archive (bundle) file.

    FOLDER Install data from the folder path.

    Either FILE or FOLDER argument can be provided to the install command.

    --skip-bug-reports
    Skip listing of downloaded bug reports, if any.

    --allow-unauthenticated
    Don't verify GPG signatures for the data to be installed to APT. Usage of this option is highly discouraged.

    --install-src-path PATH
    Path to filesystem where we want the source packages to be installed to. Default will be a folder in your TEMPDIR.

     

    GLOBAL OPTIONS

    -h, --help
    Show help message

    --verbose
    Run in verbose mode

    --test-windows
    Developer only. Used for testing on windows

    -v, --version
    Display the version of the program

     

    EXAMPLES

    To keep your disconnected machine up-to-date, here is a typical workflow

    NOTE: argument/option handling
    apt-offline relies on argparse for argument/option parsing. To explicitly instruct apt-offline about an argument, you can pass it with the -- delimiter.

    Ex. apt-offline set --update --upgrade --install-packages wm2 -- foo.sig

    By specifying the -- delimiter, we instruct apt-offline that foo.sig is an argument to the apt-offline command and not to the --install-packages option.

    apt-offline set FILENAME
    This command will generate a signature file FILENAME for APT Package Database. To generate only the signature for updates, use the --update option. To generate only the signature for package upgrades, use the --upgrade option. Default behavior when no options are specified is to generate a signature for both the operations.

    apt-offline get FILENAME
    This command will fetch the data required for APT Package Database as per the signature file FILENAME generated by apt-offline get. To download bug reports also use the --bug-reports option. Currently supported bug tracker is Debian BTS only. By default, if neither of -d or --bundle options are specified, apt-offline downloads data into a folder inside the TEMPDIR environment folder in the format apt-offline-downloads-PID, where PID is the PID of the running apt-offline process. Example on a linux machine would be something like: /tmp/apt-offline-downloads-23242/

    apt-offline install FILE|FOLDER
    This command will sync the data downloaded by apt-offline get to the APT Package Database and update it. Depending on where the data was downloaded to or packed into, either the absolute FOLDER path or the archive FILE path can be specified.

     

    AUTHOR

    apt-offline is written by Ritesh Raj Sarraf (rrs@researchut.com)

    If you wish to report a bug in apt-offline, please see http://apt-offline.alioth.debian.org or send an email to me at rrs@researchut.com

     

    SEE ALSO

    apt-get(8), apt-cache(8), dpkg(8), aptitude(8),

     

    DEDICATION

    This software is dedicated to the memory of my father Santosh Kumar Sarraf. We miss you a lot.


     

    Index

    NAME
    SYNOPSIS
    DESCRIPTION
    set FILENAME [-h] [--install-packages PKG...PKG_N] [--install-src-packages PKG...PKG_N] --src-build-dep [--release release_name] [--update] [--upgrade] [--upgrade-type upgrade]
    get FILENAME [-h] [--socket-timeout ] [-d / --download-dir] [-s / --cache-dir] [--no-checksum] [-t / --threads ] [--bundle] [--bug-reports] [--proxy-host] [--proxy-port]
    install FILE/FOLDER [-h] [--skip-bug-reports ] [--install-src-path PATH] [--allow-unauthenticated]
    GLOBAL OPTIONS
    EXAMPLES
    AUTHOR
    SEE ALSO
    DEDICATION

    This document was created by man2html, using the manual pages.
    Time: 12:40:17 GMT, April 25, 2012 apt-offline-1.3.1/THANKS0000644000000000000000000000030711212263364011521 0ustar These are, namely some of the people, who've helped me a lot in my process of learning programming. *) Peter Otten *) Duncan Booth *) Simon Forman *) Dennis Lee Bieber *) Any others whom I've missed