Example #1
0
    def decode(self, raw_segment_path, temp_dir, download_path):
        #print 'decoding', raw_segment_path
        try:
            decoded_data, metadata = yenc_decode(raw_segment_path)
        except IndexError:
            # Empty/broken file; most likely a broken segment.
            return
        except ValueError:
            # Something went wrong.
            return
        except IOError:
            # No such file.
            return

        if not decoded_data or not metadata:
            # Return for now; later do something about the file tracker count.
            return
        
        decoded_data = ''.join(decoded_data)

        # Actual decoded data's checksum.
        checksum = binascii.hexlify(struct.pack('!l', binascii.crc32(decoded_data)))

        # Get key values here.
        dest_filename = metadata.get('name')
        segment_part_size = metadata.get('size')
        total_parts = metadata.get('total')
        segment_part = metadata.get('part', 1)
        segment_end_size = metadata.get('end', 1)

        if 'crc32' in metadata:
            # Single-part files do not have pcrc32 keys.
            crc_key = metadata.get('crc32')
        else:
            crc_key = metadata.get('pcrc32')

        if len(crc_key) < 8:
            crc_key = crc_key.rjust(8, '0')

        if crc_key.lower() != checksum.lower():
            print 'broken: expected: %s, got: %s' % (crc_key, checksum)
        
        segment_filename = os.path.join(temp_dir, '%s.%s' % (dest_filename, segment_part))
        with open(segment_filename, 'wb') as segment_fp:
            segment_fp.write(decoded_data)

        # Cleanup.
        os.remove(raw_segment_path)

        if dest_filename not in self.tracker:
            self.tracker.setdefault(dest_filename, SegmentTracker())

        if (total_parts and total_parts == segment_part) or (segment_part_size == segment_end_size):
            # Found the last segment number.
            self.tracker[dest_filename].expected = int(segment_part)
            
        self.tracker[dest_filename].count += 1
        
        if self.tracker[dest_filename].is_complete():
            self.join_files(dest_filename, temp_dir, download_path)
Example #2
0
    def _get_nzb_file_size(self, nzb_file):
        try:
            if not self.aux_connection:
                self.aux_connection = nntplib.NNTP(self.server, self.port, self.username, self.password)
                self.aux_group = ""

            # switch groups if necessary
            if self.aux_group != nzb_file.groups[0]:
                self.aux_group = nzb_file.groups[0]
                response, count, first, last, name = connection.group(self.aux_group)

            # download the segment
            response, number, id, encoded_lines = connection.body('<' + nzb_file.segments[0].message_id + '>')

            decode_result = yenc_decode(encoded_lines)
            if not decode_result:
                raise Exception("malformed yEnc file: " + encoded_lines)
            decoded_segment, part_number, part_begin, part_end, file_size = decode_result

            return file_size

        except (nntplib.NNTPTemporaryError, nntplib.NNTPPermanentError), ex:
            code = ex.response[:3]
            # authorization rejected
            if code == "452" or code == "502" or code == "480":
                raise Exception("Sending newsgroup command", "NNTP authorization failed! Check username and password.")

            # invalid article or article not found
            elif code == "423" or code == "430":
                raise Exception("Article " + nzb_file.segments[0].message_id + " not found.")
Example #3
0
    def _decode_loop(self):
        try:
            current_status = STATUS_DOWNLOADING

            while 1:
                try:
                    filename, nzb_segment, encoded_lines, duration = self.decode_queue.get(True, 5)
                except Queue.Empty:
                    # if all download threads are dead, abort
                    all_threads_dead = True
                    for thread in self.threads:
                        if thread.isAlive():
                            all_threads_dead = False

                    if all_threads_dead:
                        return

                    # check for user cancellation
                    if self._update_progress("Waiting to decode a segment", current_status):
                        return

                    # continue to wait for a segment to decode
                    continue

                downloaded_file = self.downloaded_files[filename]

                try:
                    # decode the segment and capture part metadata
                    #print "Decoding " + filename + " : " + encoded_lines[0]
                    #print encoded_lines[1]
                    decode_result = yenc_decode(encoded_lines)
                    if not decode_result:
                        raise Exception("malformed yEnc file: " + encoded_lines[0])
                    decoded_segment, part_number, part_begin, part_end, file_size = decode_result
                except Exception, ex:
                    current_status = STATUS_BROKEN
                    if self._update_progress("Part " + `nzb_segment.number` + " is missing or corrupt.", current_status, filename,
                                             self.finished_files, len(self.sorted_filenames),
                                             downloaded_file.finished_bytes, downloaded_file.total_bytes):
                        return
                    continue

                if part_number != nzb_segment.number:
                    print "Warning: decoded part number " + `part_number` + " does not match requested segment number " + `nzb_segment.number`

                # first part decoded for a file sets the total file size;
                # each part is tested to make sure the file size is equal
                if downloaded_file.total_bytes == 0:
                    downloaded_file.total_bytes = file_size
                elif downloaded_file.total_bytes != file_size:
                    print "Warning: decoded part number " + `part_number` + " reports file size different from first part."

                current_file = filename
                if self._update_progress(downloaded_file.name + ":" + `nzb_segment.number`, current_status, current_file,
                                         self.finished_files, len(self.sorted_filenames),
                                         downloaded_file.finished_bytes, downloaded_file.total_bytes):
                     return
Example #4
0
    def _get_nzb_file_size(self, nzb_file):
        try:
            if not self.aux_connection:
                self.aux_connection = nntplib.NNTP(self.server, self.port,
                                                   self.username,
                                                   self.password)
                self.aux_group = ""

            # switch groups if necessary
            if self.aux_group != nzb_file.groups[0]:
                self.aux_group = nzb_file.groups[0]
                response, count, first, last, name = connection.group(
                    self.aux_group)

            # download the segment
            response, number, id, encoded_lines = connection.body(
                '<' + nzb_file.segments[0].message_id + '>')

            decode_result = yenc_decode(encoded_lines)
            if not decode_result:
                raise Exception("malformed yEnc file: " + encoded_lines)
            decoded_segment, part_number, part_begin, part_end, file_size = decode_result

            return file_size

        except (nntplib.NNTPTemporaryError, nntplib.NNTPPermanentError), ex:
            code = ex.response[:3]
            # authorization rejected
            if code == "452" or code == "502" or code == "480":
                raise Exception(
                    "Sending newsgroup command",
                    "NNTP authorization failed! Check username and password.")

            # invalid article or article not found
            elif code == "423" or code == "430":
                raise Exception("Article " + nzb_file.segments[0].message_id +
                                " not found.")
Example #5
0
    def _decode_loop(self):
        try:
            current_status = STATUS_DOWNLOADING

            while 1:
                try:
                    filename, nzb_segment, encoded_lines, duration = self.decode_queue.get(
                        True, 5)
                except Queue.Empty:
                    # if all download threads are dead, abort
                    all_threads_dead = True
                    for thread in self.threads:
                        if thread.isAlive():
                            all_threads_dead = False

                    if all_threads_dead:
                        return

                    # check for user cancellation
                    if self._update_progress("Waiting to decode a segment",
                                             current_status):
                        return

                    # continue to wait for a segment to decode
                    continue

                downloaded_file = self.downloaded_files[filename]

                try:
                    # decode the segment and capture part metadata
                    #print "Decoding " + filename + " : " + encoded_lines[0]
                    #print encoded_lines[1]
                    decode_result = yenc_decode(encoded_lines)
                    if not decode_result:
                        raise Exception("malformed yEnc file: " +
                                        encoded_lines[0])
                    decoded_segment, part_number, part_begin, part_end, file_size = decode_result
                except Exception, ex:
                    current_status = STATUS_BROKEN
                    if self._update_progress(
                            "Part " + ` nzb_segment.number ` +
                            " is missing or corrupt.", current_status,
                            filename, self.finished_files,
                            len(self.sorted_filenames),
                            downloaded_file.finished_bytes,
                            downloaded_file.total_bytes):
                        return
                    continue

                if part_number != nzb_segment.number:
                    print "Warning: decoded part number " + ` part_number ` + " does not match requested segment number " + ` nzb_segment.number `

                # first part decoded for a file sets the total file size;
                # each part is tested to make sure the file size is equal
                if downloaded_file.total_bytes == 0:
                    downloaded_file.total_bytes = file_size
                elif downloaded_file.total_bytes != file_size:
                    print "Warning: decoded part number " + ` part_number ` + " reports file size different from first part."

                current_file = filename
                if self._update_progress(
                        downloaded_file.name + ":" + ` nzb_segment.number `,
                        current_status, current_file, self.finished_files,
                        len(self.sorted_filenames),
                        downloaded_file.finished_bytes,
                        downloaded_file.total_bytes):
                    return
Example #6
0
 def test_yenc_decode(self):
     yenc_decode()