diff --git a/README.md b/README.md index 52ab685..8c5d116 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,17 @@ IPFS transport for apt sudo apt install python3 pip sudo pip3 install ipfsapi + sudo snap install ipfs + ipfs init + ipfs daemon & # Install Copy the ipfs file from this repo to the directory for apt transport methods: sudo wget --output-file /usr/lib/apt/methods/ipfs https://raw.githubusercontent.com/JaquerEspeis/apt-transport-ipfs/master/ipfs + sudo chmod +x /usr/lib/apt/methods/ipfs # Configure -Add to your apt sources.list file an IPFS mirror. TODO set up a mirror. +Add an IPFS mirror to your apt sources.list file. TODO set up a mirror. diff --git a/ipfs b/ipfs index 26e0a72..42cbf7c 100755 --- a/ipfs +++ b/ipfs @@ -20,7 +20,12 @@ # This is based on apt-transport-s3: # https://github.com/BashtonLtd/apt-transport-s3 +import hashlib +import os import sys +import tempfile + +import ipfsapi class IPFS_method(): @@ -33,15 +38,119 @@ class IPFS_method(): def send_capabilities(self): self._send(100, { 'Version': '1.1', - 'Single-Instance': 'true', - 'Send-Config': 'true'}) + 'Single-Instance': 'true'}) + + def send_status(self, headers): + self._send(102, headers) + + def send_uri_start(self, headers): + self._send(200, headers) + + def send_uri_done(self, headers): + self._send(201, headers) + + def send_uri_failure(self, headers): + self._send(400, headers) def _send(self, code, headers): message = APTMessage(code, headers) sys.stdout.write(message.encode()) def run(self): - pass + """Loop through requests on stdin""" + while True: + message = self._read_message() + if message is None: + return 0 + if message['number'] == 600: + try: + self.fetch(message) + except Exception as e: + self.send_uri_failure({ + 'URI': self.uri, + 'Message': e.__class__.__name__ + ": " + str(e)}) + else: + return 100 + + def _read_message(self): + """Read an apt message. + + Apt uses for communication with its methods the text protocol similar + to http. This function parses the protocol messages from stdin. + + """ + if self.__eof: + return None + result = {} + line = sys.stdin.readline() + while line == '\n': + line = sys.stdin.readline() + if not line: + self.__eof = True + return None + s = line.split(" ", 1) + result['number'] = int(s[0]) + result['text'] = s[1].strip() + while not self.__eof: + line = sys.stdin.readline() + if not line: + self.__eof = True + return result + if line == '\n': + return result + (item, value) = line.split(":", 1) + if not result.get(item): + result[item] = [] + result[item].append(value.strip()) + return result + + def fetch(self, message): + self.uri = message['URI'][0] + self.filename = message['Filename'][0] + + self.send_status({'URI': self.uri, 'Message': 'Waiting for stats'}) + ipfs = ipfsapi.connect('127.0.0.1', 5001) + inrelease = ( + 'QmabnVr8k4uFdwQ8dW2P3jEUxdVPF8FcAgc2i8q7T2ArMg/' + 'dists/xenial/InRelease') + stat = ipfs.object_stat(inrelease) + self.send_uri_start({ + 'URI': self.uri, + # FIXME We can't get the real size without downloading the file. + # https://github.com/ipfs/go-ipfs/issues/2071 + # --elopio - 20171203 + 'Size': stat['CumulativeSize']}) + + # XXX IPFS downloads the file to the current directory. + # --elopio - 20171203 + tmp_dir = tempfile.gettempdir() + os.chdir(tmp_dir) + fetched_file_path = os.path.join( + tmp_dir, os.path.basename(inrelease)) + cwd = os.getcwd() + try: + ipfs.get(inrelease) + finally: + os.chdir(cwd) + os.rename(fetched_file_path, self.filename) + + hash_md5 = hashlib.md5() + hash_sha256 = hashlib.sha256() + hash_sha512 = hashlib.sha512() + with open(self.filename, 'rb') as fetched_file: + for chunk in iter(lambda: fetched_file.read(4096), b''): + hash_md5.update(chunk) + hash_sha256.update(chunk) + hash_sha512.update(chunk) + + self.send_uri_done({ + 'URI': self.uri, + 'Filename': self.filename, + 'Size': os.stat(self.filename).st_size, + 'MD5-Hash': hash_md5.hexdigest(), + 'MD5Sum-Hash': hash_md5.hexdigest(), + 'SHA256-Hash': hash_sha256.hexdigest(), + 'SHA512-Hash': hash_sha512.hexdigest()}) class APTMessage():