├── .gitignore ├── README.md ├── biplist └── __init__.py ├── ios-url-scheme-extractor.py └── six.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Python ### 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | 59 | 60 | ### OSX ### 61 | .DS_Store 62 | .AppleDouble 63 | .LSOverride 64 | 65 | # Icon must end with two \r 66 | Icon 67 | 68 | 69 | # Thumbnails 70 | ._* 71 | 72 | # Files that might appear on external disk 73 | .Spotlight-V100 74 | .Trashes 75 | 76 | # Directories potentially created on remote AFP share 77 | .AppleDB 78 | .AppleDesktop 79 | Network Trash Folder 80 | Temporary Items 81 | .apdisk 82 | 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Simple tkinter based url scheme extractor for iOS. 2 | 3 | ## Run 4 | 5 | ```sh 6 | $ python ios-url-scheme-extractor.py 7 | ``` 8 | --- 9 | 10 | © 2014 [Buzzvil](http://www.buzzvil.com), shared under the [MIT license](http://www.opensource.org/licenses/MIT). 11 | -------------------------------------------------------------------------------- /biplist/__init__.py: -------------------------------------------------------------------------------- 1 | """biplist -- a library for reading and writing binary property list files. 2 | 3 | Binary Property List (plist) files provide a faster and smaller serialization 4 | format for property lists on OS X. This is a library for generating binary 5 | plists which can be read by OS X, iOS, or other clients. 6 | 7 | The API models the plistlib API, and will call through to plistlib when 8 | XML serialization or deserialization is required. 9 | 10 | To generate plists with UID values, wrap the values with the Uid object. The 11 | value must be an int. 12 | 13 | To generate plists with NSData/CFData values, wrap the values with the 14 | Data object. The value must be a string. 15 | 16 | Date values can only be datetime.datetime objects. 17 | 18 | The exceptions InvalidPlistException and NotBinaryPlistException may be 19 | thrown to indicate that the data cannot be serialized or deserialized as 20 | a binary plist. 21 | 22 | Plist generation example: 23 | 24 | from biplist import * 25 | from datetime import datetime 26 | plist = {'aKey':'aValue', 27 | '0':1.322, 28 | 'now':datetime.now(), 29 | 'list':[1,2,3], 30 | 'tuple':('a','b','c') 31 | } 32 | try: 33 | writePlist(plist, "example.plist") 34 | except (InvalidPlistException, NotBinaryPlistException), e: 35 | print "Something bad happened:", e 36 | 37 | Plist parsing example: 38 | 39 | from biplist import * 40 | try: 41 | plist = readPlist("example.plist") 42 | print plist 43 | except (InvalidPlistException, NotBinaryPlistException), e: 44 | print "Not a plist:", e 45 | """ 46 | 47 | import sys 48 | from collections import namedtuple 49 | import calendar 50 | import datetime 51 | import math 52 | import plistlib 53 | from struct import pack, unpack 54 | import sys 55 | import time 56 | 57 | import six 58 | 59 | __all__ = [ 60 | 'Uid', 'Data', 'readPlist', 'writePlist', 'readPlistFromString', 61 | 'writePlistToString', 'InvalidPlistException', 'NotBinaryPlistException' 62 | ] 63 | 64 | apple_reference_date_offset = 978307200 65 | 66 | class Uid(int): 67 | """Wrapper around integers for representing UID values. This 68 | is used in keyed archiving.""" 69 | def __repr__(self): 70 | return "Uid(%d)" % self 71 | 72 | class Data(six.binary_type): 73 | """Wrapper around str types for representing Data values.""" 74 | pass 75 | 76 | class InvalidPlistException(Exception): 77 | """Raised when the plist is incorrectly formatted.""" 78 | pass 79 | 80 | class NotBinaryPlistException(Exception): 81 | """Raised when a binary plist was expected but not encountered.""" 82 | pass 83 | 84 | def readPlist(pathOrFile): 85 | """Raises NotBinaryPlistException, InvalidPlistException""" 86 | didOpen = False 87 | result = None 88 | if isinstance(pathOrFile, (six.binary_type, six.text_type)): 89 | pathOrFile = open(pathOrFile, 'rb') 90 | didOpen = True 91 | try: 92 | reader = PlistReader(pathOrFile) 93 | result = reader.parse() 94 | except NotBinaryPlistException as e: 95 | try: 96 | pathOrFile.seek(0) 97 | result = plistlib.readPlist(pathOrFile) 98 | result = wrapDataObject(result, for_binary=True) 99 | except Exception as e: 100 | raise InvalidPlistException(e) 101 | if didOpen: 102 | pathOrFile.close() 103 | return result 104 | 105 | def wrapDataObject(o, for_binary=False): 106 | if isinstance(o, Data) and not for_binary: 107 | o = plistlib.Data(o) 108 | elif isinstance(o, plistlib.Data) and for_binary: 109 | o = Data(o.data) 110 | elif isinstance(o, tuple): 111 | o = wrapDataObject(list(o), for_binary) 112 | o = tuple(o) 113 | elif isinstance(o, list): 114 | for i in range(len(o)): 115 | o[i] = wrapDataObject(o[i], for_binary) 116 | elif isinstance(o, dict): 117 | for k in o: 118 | o[k] = wrapDataObject(o[k], for_binary) 119 | return o 120 | 121 | def writePlist(rootObject, pathOrFile, binary=True): 122 | if not binary: 123 | rootObject = wrapDataObject(rootObject, binary) 124 | return plistlib.writePlist(rootObject, pathOrFile) 125 | else: 126 | didOpen = False 127 | if isinstance(pathOrFile, (six.binary_type, six.text_type)): 128 | pathOrFile = open(pathOrFile, 'wb') 129 | didOpen = True 130 | writer = PlistWriter(pathOrFile) 131 | result = writer.writeRoot(rootObject) 132 | if didOpen: 133 | pathOrFile.close() 134 | return result 135 | 136 | def readPlistFromString(data): 137 | return readPlist(six.BytesIO(data)) 138 | 139 | def writePlistToString(rootObject, binary=True): 140 | if not binary: 141 | rootObject = wrapDataObject(rootObject, binary) 142 | if six.PY3: 143 | return plistlib.writePlistToBytes(rootObject) 144 | else: 145 | return plistlib.writePlistToString(rootObject) 146 | else: 147 | io = six.BytesIO() 148 | writer = PlistWriter(io) 149 | writer.writeRoot(rootObject) 150 | return io.getvalue() 151 | 152 | def is_stream_binary_plist(stream): 153 | stream.seek(0) 154 | header = stream.read(7) 155 | if header == six.b('bplist0'): 156 | return True 157 | else: 158 | return False 159 | 160 | PlistTrailer = namedtuple('PlistTrailer', 'offsetSize, objectRefSize, offsetCount, topLevelObjectNumber, offsetTableOffset') 161 | PlistByteCounts = namedtuple('PlistByteCounts', 'nullBytes, boolBytes, intBytes, realBytes, dateBytes, dataBytes, stringBytes, uidBytes, arrayBytes, setBytes, dictBytes') 162 | 163 | class PlistReader(object): 164 | file = None 165 | contents = '' 166 | offsets = None 167 | trailer = None 168 | currentOffset = 0 169 | 170 | def __init__(self, fileOrStream): 171 | """Raises NotBinaryPlistException.""" 172 | self.reset() 173 | self.file = fileOrStream 174 | 175 | def parse(self): 176 | return self.readRoot() 177 | 178 | def reset(self): 179 | self.trailer = None 180 | self.contents = '' 181 | self.offsets = [] 182 | self.currentOffset = 0 183 | 184 | def readRoot(self): 185 | result = None 186 | self.reset() 187 | # Get the header, make sure it's a valid file. 188 | if not is_stream_binary_plist(self.file): 189 | raise NotBinaryPlistException() 190 | self.file.seek(0) 191 | self.contents = self.file.read() 192 | if len(self.contents) < 32: 193 | raise InvalidPlistException("File is too short.") 194 | trailerContents = self.contents[-32:] 195 | try: 196 | self.trailer = PlistTrailer._make(unpack("!xxxxxxBBQQQ", trailerContents)) 197 | offset_size = self.trailer.offsetSize * self.trailer.offsetCount 198 | offset = self.trailer.offsetTableOffset 199 | offset_contents = self.contents[offset:offset+offset_size] 200 | offset_i = 0 201 | while offset_i < self.trailer.offsetCount: 202 | begin = self.trailer.offsetSize*offset_i 203 | tmp_contents = offset_contents[begin:begin+self.trailer.offsetSize] 204 | tmp_sized = self.getSizedInteger(tmp_contents, self.trailer.offsetSize) 205 | self.offsets.append(tmp_sized) 206 | offset_i += 1 207 | self.setCurrentOffsetToObjectNumber(self.trailer.topLevelObjectNumber) 208 | result = self.readObject() 209 | except TypeError as e: 210 | raise InvalidPlistException(e) 211 | return result 212 | 213 | def setCurrentOffsetToObjectNumber(self, objectNumber): 214 | self.currentOffset = self.offsets[objectNumber] 215 | 216 | def readObject(self): 217 | result = None 218 | tmp_byte = self.contents[self.currentOffset:self.currentOffset+1] 219 | marker_byte = unpack("!B", tmp_byte)[0] 220 | format = (marker_byte >> 4) & 0x0f 221 | extra = marker_byte & 0x0f 222 | self.currentOffset += 1 223 | 224 | def proc_extra(extra): 225 | if extra == 0b1111: 226 | #self.currentOffset += 1 227 | extra = self.readObject() 228 | return extra 229 | 230 | # bool, null, or fill byte 231 | if format == 0b0000: 232 | if extra == 0b0000: 233 | result = None 234 | elif extra == 0b1000: 235 | result = False 236 | elif extra == 0b1001: 237 | result = True 238 | elif extra == 0b1111: 239 | pass # fill byte 240 | else: 241 | raise InvalidPlistException("Invalid object found at offset: %d" % (self.currentOffset - 1)) 242 | # int 243 | elif format == 0b0001: 244 | extra = proc_extra(extra) 245 | result = self.readInteger(pow(2, extra)) 246 | # real 247 | elif format == 0b0010: 248 | extra = proc_extra(extra) 249 | result = self.readReal(extra) 250 | # date 251 | elif format == 0b0011 and extra == 0b0011: 252 | result = self.readDate() 253 | # data 254 | elif format == 0b0100: 255 | extra = proc_extra(extra) 256 | result = self.readData(extra) 257 | # ascii string 258 | elif format == 0b0101: 259 | extra = proc_extra(extra) 260 | result = self.readAsciiString(extra) 261 | # Unicode string 262 | elif format == 0b0110: 263 | extra = proc_extra(extra) 264 | result = self.readUnicode(extra) 265 | # uid 266 | elif format == 0b1000: 267 | result = self.readUid(extra) 268 | # array 269 | elif format == 0b1010: 270 | extra = proc_extra(extra) 271 | result = self.readArray(extra) 272 | # set 273 | elif format == 0b1100: 274 | extra = proc_extra(extra) 275 | result = set(self.readArray(extra)) 276 | # dict 277 | elif format == 0b1101: 278 | extra = proc_extra(extra) 279 | result = self.readDict(extra) 280 | else: 281 | raise InvalidPlistException("Invalid object found: {format: %s, extra: %s}" % (bin(format), bin(extra))) 282 | return result 283 | 284 | def readInteger(self, bytes): 285 | result = 0 286 | original_offset = self.currentOffset 287 | data = self.contents[self.currentOffset:self.currentOffset+bytes] 288 | result = self.getSizedInteger(data, bytes) 289 | self.currentOffset = original_offset + bytes 290 | return result 291 | 292 | def readReal(self, length): 293 | result = 0.0 294 | to_read = pow(2, length) 295 | data = self.contents[self.currentOffset:self.currentOffset+to_read] 296 | if length == 2: # 4 bytes 297 | result = unpack('>f', data)[0] 298 | elif length == 3: # 8 bytes 299 | result = unpack('>d', data)[0] 300 | else: 301 | raise InvalidPlistException("Unknown real of length %d bytes" % to_read) 302 | return result 303 | 304 | def readRefs(self, count): 305 | refs = [] 306 | i = 0 307 | while i < count: 308 | fragment = self.contents[self.currentOffset:self.currentOffset+self.trailer.objectRefSize] 309 | ref = self.getSizedInteger(fragment, len(fragment)) 310 | refs.append(ref) 311 | self.currentOffset += self.trailer.objectRefSize 312 | i += 1 313 | return refs 314 | 315 | def readArray(self, count): 316 | result = [] 317 | values = self.readRefs(count) 318 | i = 0 319 | while i < len(values): 320 | self.setCurrentOffsetToObjectNumber(values[i]) 321 | value = self.readObject() 322 | result.append(value) 323 | i += 1 324 | return result 325 | 326 | def readDict(self, count): 327 | result = {} 328 | keys = self.readRefs(count) 329 | values = self.readRefs(count) 330 | i = 0 331 | while i < len(keys): 332 | self.setCurrentOffsetToObjectNumber(keys[i]) 333 | key = self.readObject() 334 | self.setCurrentOffsetToObjectNumber(values[i]) 335 | value = self.readObject() 336 | result[key] = value 337 | i += 1 338 | return result 339 | 340 | def readAsciiString(self, length): 341 | result = unpack("!%ds" % length, self.contents[self.currentOffset:self.currentOffset+length])[0] 342 | self.currentOffset += length 343 | return result 344 | 345 | def readUnicode(self, length): 346 | actual_length = length*2 347 | data = self.contents[self.currentOffset:self.currentOffset+actual_length] 348 | # unpack not needed?!! data = unpack(">%ds" % (actual_length), data)[0] 349 | self.currentOffset += actual_length 350 | return data.decode('utf_16_be') 351 | 352 | def readDate(self): 353 | global apple_reference_date_offset 354 | result = unpack(">d", self.contents[self.currentOffset:self.currentOffset+8])[0] 355 | result = datetime.datetime.utcfromtimestamp(result + apple_reference_date_offset) 356 | self.currentOffset += 8 357 | return result 358 | 359 | def readData(self, length): 360 | result = self.contents[self.currentOffset:self.currentOffset+length] 361 | self.currentOffset += length 362 | return Data(result) 363 | 364 | def readUid(self, length): 365 | return Uid(self.readInteger(length+1)) 366 | 367 | def getSizedInteger(self, data, bytes): 368 | result = 0 369 | # 1, 2, and 4 byte integers are unsigned 370 | if bytes == 1: 371 | result = unpack('>B', data)[0] 372 | elif bytes == 2: 373 | result = unpack('>H', data)[0] 374 | elif bytes == 4: 375 | result = unpack('>L', data)[0] 376 | elif bytes == 8: 377 | result = unpack('>q', data)[0] 378 | else: 379 | raise InvalidPlistException("Encountered integer longer than 8 bytes.") 380 | return result 381 | 382 | class HashableWrapper(object): 383 | def __init__(self, value): 384 | self.value = value 385 | def __repr__(self): 386 | return "" % [self.value] 387 | 388 | class BoolWrapper(object): 389 | def __init__(self, value): 390 | self.value = value 391 | def __repr__(self): 392 | return "" % self.value 393 | 394 | class PlistWriter(object): 395 | header = six.b('bplist00bybiplist1.0') 396 | file = None 397 | byteCounts = None 398 | trailer = None 399 | computedUniques = None 400 | writtenReferences = None 401 | referencePositions = None 402 | wrappedTrue = None 403 | wrappedFalse = None 404 | 405 | def __init__(self, file): 406 | self.reset() 407 | self.file = file 408 | self.wrappedTrue = BoolWrapper(True) 409 | self.wrappedFalse = BoolWrapper(False) 410 | 411 | def reset(self): 412 | self.byteCounts = PlistByteCounts(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) 413 | self.trailer = PlistTrailer(0, 0, 0, 0, 0) 414 | 415 | # A set of all the uniques which have been computed. 416 | self.computedUniques = set() 417 | # A list of all the uniques which have been written. 418 | self.writtenReferences = {} 419 | # A dict of the positions of the written uniques. 420 | self.referencePositions = {} 421 | 422 | def positionOfObjectReference(self, obj): 423 | """If the given object has been written already, return its 424 | position in the offset table. Otherwise, return None.""" 425 | return self.writtenReferences.get(obj) 426 | 427 | def writeRoot(self, root): 428 | """ 429 | Strategy is: 430 | - write header 431 | - wrap root object so everything is hashable 432 | - compute size of objects which will be written 433 | - need to do this in order to know how large the object refs 434 | will be in the list/dict/set reference lists 435 | - write objects 436 | - keep objects in writtenReferences 437 | - keep positions of object references in referencePositions 438 | - write object references with the length computed previously 439 | - computer object reference length 440 | - write object reference positions 441 | - write trailer 442 | """ 443 | output = self.header 444 | wrapped_root = self.wrapRoot(root) 445 | should_reference_root = True#not isinstance(wrapped_root, HashableWrapper) 446 | self.computeOffsets(wrapped_root, asReference=should_reference_root, isRoot=True) 447 | self.trailer = self.trailer._replace(**{'objectRefSize':self.intSize(len(self.computedUniques))}) 448 | (_, output) = self.writeObjectReference(wrapped_root, output) 449 | output = self.writeObject(wrapped_root, output, setReferencePosition=True) 450 | 451 | # output size at this point is an upper bound on how big the 452 | # object reference offsets need to be. 453 | self.trailer = self.trailer._replace(**{ 454 | 'offsetSize':self.intSize(len(output)), 455 | 'offsetCount':len(self.computedUniques), 456 | 'offsetTableOffset':len(output), 457 | 'topLevelObjectNumber':0 458 | }) 459 | 460 | output = self.writeOffsetTable(output) 461 | output += pack('!xxxxxxBBQQQ', *self.trailer) 462 | self.file.write(output) 463 | 464 | def wrapRoot(self, root): 465 | if isinstance(root, bool): 466 | if root is True: 467 | return self.wrappedTrue 468 | else: 469 | return self.wrappedFalse 470 | elif isinstance(root, set): 471 | n = set() 472 | for value in root: 473 | n.add(self.wrapRoot(value)) 474 | return HashableWrapper(n) 475 | elif isinstance(root, dict): 476 | n = {} 477 | for key, value in six.iteritems(root): 478 | n[self.wrapRoot(key)] = self.wrapRoot(value) 479 | return HashableWrapper(n) 480 | elif isinstance(root, list): 481 | n = [] 482 | for value in root: 483 | n.append(self.wrapRoot(value)) 484 | return HashableWrapper(n) 485 | elif isinstance(root, tuple): 486 | n = tuple([self.wrapRoot(value) for value in root]) 487 | return HashableWrapper(n) 488 | else: 489 | return root 490 | 491 | def incrementByteCount(self, field, incr=1): 492 | self.byteCounts = self.byteCounts._replace(**{field:self.byteCounts.__getattribute__(field) + incr}) 493 | 494 | def computeOffsets(self, obj, asReference=False, isRoot=False): 495 | def check_key(key): 496 | if key is None: 497 | raise InvalidPlistException('Dictionary keys cannot be null in plists.') 498 | elif isinstance(key, Data): 499 | raise InvalidPlistException('Data cannot be dictionary keys in plists.') 500 | elif not isinstance(key, (six.binary_type, six.text_type)): 501 | raise InvalidPlistException('Keys must be strings.') 502 | 503 | def proc_size(size): 504 | if size > 0b1110: 505 | size += self.intSize(size) 506 | return size 507 | # If this should be a reference, then we keep a record of it in the 508 | # uniques table. 509 | if asReference: 510 | if obj in self.computedUniques: 511 | return 512 | else: 513 | self.computedUniques.add(obj) 514 | 515 | if obj is None: 516 | self.incrementByteCount('nullBytes') 517 | elif isinstance(obj, BoolWrapper): 518 | self.incrementByteCount('boolBytes') 519 | elif isinstance(obj, Uid): 520 | size = self.intSize(obj) 521 | self.incrementByteCount('uidBytes', incr=1+size) 522 | elif isinstance(obj, six.integer_types): 523 | size = self.intSize(obj) 524 | self.incrementByteCount('intBytes', incr=1+size) 525 | elif isinstance(obj, (float)): 526 | size = self.realSize(obj) 527 | self.incrementByteCount('realBytes', incr=1+size) 528 | elif isinstance(obj, datetime.datetime): 529 | self.incrementByteCount('dateBytes', incr=2) 530 | elif isinstance(obj, Data): 531 | size = proc_size(len(obj)) 532 | self.incrementByteCount('dataBytes', incr=1+size) 533 | elif isinstance(obj, (six.text_type, six.binary_type)): 534 | size = proc_size(len(obj)) 535 | self.incrementByteCount('stringBytes', incr=1+size) 536 | elif isinstance(obj, HashableWrapper): 537 | obj = obj.value 538 | if isinstance(obj, set): 539 | size = proc_size(len(obj)) 540 | self.incrementByteCount('setBytes', incr=1+size) 541 | for value in obj: 542 | self.computeOffsets(value, asReference=True) 543 | elif isinstance(obj, (list, tuple)): 544 | size = proc_size(len(obj)) 545 | self.incrementByteCount('arrayBytes', incr=1+size) 546 | for value in obj: 547 | asRef = True 548 | self.computeOffsets(value, asReference=True) 549 | elif isinstance(obj, dict): 550 | size = proc_size(len(obj)) 551 | self.incrementByteCount('dictBytes', incr=1+size) 552 | for key, value in six.iteritems(obj): 553 | check_key(key) 554 | self.computeOffsets(key, asReference=True) 555 | self.computeOffsets(value, asReference=True) 556 | else: 557 | raise InvalidPlistException("Unknown object type.") 558 | 559 | def writeObjectReference(self, obj, output): 560 | """Tries to write an object reference, adding it to the references 561 | table. Does not write the actual object bytes or set the reference 562 | position. Returns a tuple of whether the object was a new reference 563 | (True if it was, False if it already was in the reference table) 564 | and the new output. 565 | """ 566 | position = self.positionOfObjectReference(obj) 567 | if position is None: 568 | self.writtenReferences[obj] = len(self.writtenReferences) 569 | output += self.binaryInt(len(self.writtenReferences) - 1, bytes=self.trailer.objectRefSize) 570 | return (True, output) 571 | else: 572 | output += self.binaryInt(position, bytes=self.trailer.objectRefSize) 573 | return (False, output) 574 | 575 | def writeObject(self, obj, output, setReferencePosition=False): 576 | """Serializes the given object to the output. Returns output. 577 | If setReferencePosition is True, will set the position the 578 | object was written. 579 | """ 580 | def proc_variable_length(format, length): 581 | result = six.b('') 582 | if length > 0b1110: 583 | result += pack('!B', (format << 4) | 0b1111) 584 | result = self.writeObject(length, result) 585 | else: 586 | result += pack('!B', (format << 4) | length) 587 | return result 588 | 589 | if isinstance(obj, six.text_type) and obj == six.u(''): 590 | # The Apple Plist decoder can't decode a zero length Unicode string. 591 | obj = six.b('') 592 | 593 | if setReferencePosition: 594 | self.referencePositions[obj] = len(output) 595 | 596 | if obj is None: 597 | output += pack('!B', 0b00000000) 598 | elif isinstance(obj, BoolWrapper): 599 | if obj.value is False: 600 | output += pack('!B', 0b00001000) 601 | else: 602 | output += pack('!B', 0b00001001) 603 | elif isinstance(obj, Uid): 604 | size = self.intSize(obj) 605 | output += pack('!B', (0b1000 << 4) | size - 1) 606 | output += self.binaryInt(obj) 607 | elif isinstance(obj, six.integer_types): 608 | bytes = self.intSize(obj) 609 | root = math.log(bytes, 2) 610 | output += pack('!B', (0b0001 << 4) | int(root)) 611 | output += self.binaryInt(obj) 612 | elif isinstance(obj, float): 613 | # just use doubles 614 | output += pack('!B', (0b0010 << 4) | 3) 615 | output += self.binaryReal(obj) 616 | elif isinstance(obj, datetime.datetime): 617 | timestamp = calendar.timegm(obj.utctimetuple()) 618 | timestamp -= apple_reference_date_offset 619 | output += pack('!B', 0b00110011) 620 | output += pack('!d', float(timestamp)) 621 | elif isinstance(obj, Data): 622 | output += proc_variable_length(0b0100, len(obj)) 623 | output += obj 624 | elif isinstance(obj, six.text_type): 625 | bytes = obj.encode('utf_16_be') 626 | output += proc_variable_length(0b0110, len(bytes)//2) 627 | output += bytes 628 | elif isinstance(obj, six.binary_type): 629 | bytes = obj 630 | output += proc_variable_length(0b0101, len(bytes)) 631 | output += bytes 632 | elif isinstance(obj, HashableWrapper): 633 | obj = obj.value 634 | if isinstance(obj, (set, list, tuple)): 635 | if isinstance(obj, set): 636 | output += proc_variable_length(0b1100, len(obj)) 637 | else: 638 | output += proc_variable_length(0b1010, len(obj)) 639 | 640 | objectsToWrite = [] 641 | for objRef in obj: 642 | (isNew, output) = self.writeObjectReference(objRef, output) 643 | if isNew: 644 | objectsToWrite.append(objRef) 645 | for objRef in objectsToWrite: 646 | output = self.writeObject(objRef, output, setReferencePosition=True) 647 | elif isinstance(obj, dict): 648 | output += proc_variable_length(0b1101, len(obj)) 649 | keys = [] 650 | values = [] 651 | objectsToWrite = [] 652 | for key, value in six.iteritems(obj): 653 | keys.append(key) 654 | values.append(value) 655 | for key in keys: 656 | (isNew, output) = self.writeObjectReference(key, output) 657 | if isNew: 658 | objectsToWrite.append(key) 659 | for value in values: 660 | (isNew, output) = self.writeObjectReference(value, output) 661 | if isNew: 662 | objectsToWrite.append(value) 663 | for objRef in objectsToWrite: 664 | output = self.writeObject(objRef, output, setReferencePosition=True) 665 | return output 666 | 667 | def writeOffsetTable(self, output): 668 | """Writes all of the object reference offsets.""" 669 | all_positions = [] 670 | writtenReferences = list(self.writtenReferences.items()) 671 | writtenReferences.sort(key=lambda x: x[1]) 672 | for obj,order in writtenReferences: 673 | # Porting note: Elsewhere we deliberately replace empty unicdoe strings 674 | # with empty binary strings, but the empty unicode string 675 | # goes into writtenReferences. This isn't an issue in Py2 676 | # because u'' and b'' have the same hash; but it is in 677 | # Py3, where they don't. 678 | if six.PY3 and obj == six.u(''): 679 | obj = six.b('') 680 | position = self.referencePositions.get(obj) 681 | if position is None: 682 | raise InvalidPlistException("Error while writing offsets table. Object not found. %s" % obj) 683 | output += self.binaryInt(position, self.trailer.offsetSize) 684 | all_positions.append(position) 685 | return output 686 | 687 | def binaryReal(self, obj): 688 | # just use doubles 689 | result = pack('>d', obj) 690 | return result 691 | 692 | def binaryInt(self, obj, bytes=None): 693 | result = six.b('') 694 | if bytes is None: 695 | bytes = self.intSize(obj) 696 | if bytes == 1: 697 | result += pack('>B', obj) 698 | elif bytes == 2: 699 | result += pack('>H', obj) 700 | elif bytes == 4: 701 | result += pack('>L', obj) 702 | elif bytes == 8: 703 | result += pack('>q', obj) 704 | else: 705 | raise InvalidPlistException("Core Foundation can't handle integers with size greater than 8 bytes.") 706 | return result 707 | 708 | def intSize(self, obj): 709 | """Returns the number of bytes necessary to store the given integer.""" 710 | # SIGNED 711 | if obj < 0: # Signed integer, always 8 bytes 712 | return 8 713 | # UNSIGNED 714 | elif obj <= 0xFF: # 1 byte 715 | return 1 716 | elif obj <= 0xFFFF: # 2 bytes 717 | return 2 718 | elif obj <= 0xFFFFFFFF: # 4 bytes 719 | return 4 720 | # SIGNED 721 | # 0x7FFFFFFFFFFFFFFF is the max. 722 | elif obj <= 0x7FFFFFFFFFFFFFFF: # 8 bytes 723 | return 8 724 | else: 725 | raise InvalidPlistException("Core Foundation can't handle integers with size greater than 8 bytes.") 726 | 727 | def realSize(self, obj): 728 | return 8 729 | -------------------------------------------------------------------------------- /ios-url-scheme-extractor.py: -------------------------------------------------------------------------------- 1 | import zipfile 2 | import os 3 | import fnmatch 4 | import shutil 5 | from biplist import * 6 | from sys import platform as _platform 7 | from os.path import expanduser 8 | import Tkinter,tkFileDialog 9 | 10 | def extract_url_scheme(bytes): 11 | plist = readPlistFromString(bytes) 12 | schemes = [] 13 | print plist 14 | if 'URL Types' in plist: 15 | for data in plist['URL Types']: 16 | for scheme in data.get('URL Schemes'): 17 | schemes.append(scheme) 18 | if 'CFBundleURLTypes' in plist: 19 | for data in plist['CFBundleURLTypes']: 20 | for scheme in data.get('CFBundleURLSchemes'): 21 | schemes.append(scheme) 22 | return schemes 23 | 24 | def get_url_scheme(filename): 25 | with zipfile.ZipFile(filename) as zf: 26 | for filename in zf.namelist(): 27 | if filename.endswith('Info.plist'): 28 | bytes = zf.read(filename) 29 | #print bytes 30 | schemes=extract_url_scheme(bytes) 31 | print schemes 32 | return schemes 33 | return '' 34 | 35 | class simpleapp_tk(Tkinter.Tk): 36 | def __init__(self,parent): 37 | Tkinter.Tk.__init__(self,parent) 38 | self.parent = parent 39 | self.initialize() 40 | self.textbox = [] 41 | 42 | def initialize(self): 43 | self.grid() 44 | 45 | button = Tkinter.Button(self,text=u"Open", 46 | command=self.OnButtonClick) 47 | button.grid(column=0,row=0) 48 | 49 | self.entry_vars = [] 50 | self.entries = [] 51 | for i in xrange(10): 52 | entry_var = Tkinter.StringVar() 53 | entry = Tkinter.Entry(self,textvariable=entry_var) 54 | entry.grid(column=0,row=i + 1,sticky='EW') 55 | 56 | self.entry_vars.append(entry_var) 57 | self.entries.append(entry) 58 | 59 | self.grid_columnconfigure(0,weight=1) 60 | self.resizable(True,False) 61 | 62 | def showFileOpen(self): 63 | initialdir = '' 64 | 65 | if _platform == "darwin": 66 | initialdir = expanduser("~") + '/Music/iTunes/iTunes Music/Mobile Applications' 67 | elif _platform == "win32": 68 | initialdir = expanduser("~") + '\\My Documents\\My Music\\iTunes\\iTunes Media\Mobile Applications' 69 | 70 | if not os.path.exists(initialdir): 71 | initialdir = '' 72 | 73 | file = tkFileDialog.askopenfile(parent=self.parent, 74 | mode='rb', 75 | title='Choose a file', 76 | defaultextension='ipa', 77 | initialdir=initialdir) 78 | return file 79 | 80 | def OnButtonClick(self): 81 | file = self.showFileOpen() 82 | if file != None: 83 | for entry_var in self.entry_vars: 84 | entry_var.set('') 85 | path = os.path.abspath(file.name) 86 | # print u'input: {0}'.format(path) 87 | schemes = get_url_scheme(path) 88 | # print u'output: {0}'.format(schemes) 89 | 90 | for i, scheme in enumerate(schemes): 91 | self.entry_vars[i].set(scheme + '://') 92 | 93 | # import tkMessageBox 94 | # tkMessageBox.showinfo("Result", '\n'.join(schemes)) 95 | 96 | if __name__ == "__main__": 97 | app = simpleapp_tk(None) 98 | app.title('URL Scheme extractor') 99 | app.minsize(300, 200) 100 | app.mainloop() 101 | 102 | 103 | -------------------------------------------------------------------------------- /six.py: -------------------------------------------------------------------------------- 1 | """Utilities for writing code that runs on Python 2 and 3""" 2 | 3 | # Copyright (c) 2010-2013 Benjamin Peterson 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import operator 24 | import sys 25 | import types 26 | 27 | __author__ = "Benjamin Peterson " 28 | __version__ = "1.4.1" 29 | 30 | 31 | # Useful for very coarse version differentiation. 32 | PY2 = sys.version_info[0] == 2 33 | PY3 = sys.version_info[0] == 3 34 | 35 | if PY3: 36 | string_types = str, 37 | integer_types = int, 38 | class_types = type, 39 | text_type = str 40 | binary_type = bytes 41 | 42 | MAXSIZE = sys.maxsize 43 | else: 44 | string_types = basestring, 45 | integer_types = (int, long) 46 | class_types = (type, types.ClassType) 47 | text_type = unicode 48 | binary_type = str 49 | 50 | if sys.platform.startswith("java"): 51 | # Jython always uses 32 bits. 52 | MAXSIZE = int((1 << 31) - 1) 53 | else: 54 | # It's possible to have sizeof(long) != sizeof(Py_ssize_t). 55 | class X(object): 56 | def __len__(self): 57 | return 1 << 31 58 | try: 59 | len(X()) 60 | except OverflowError: 61 | # 32-bit 62 | MAXSIZE = int((1 << 31) - 1) 63 | else: 64 | # 64-bit 65 | MAXSIZE = int((1 << 63) - 1) 66 | del X 67 | 68 | 69 | def _add_doc(func, doc): 70 | """Add documentation to a function.""" 71 | func.__doc__ = doc 72 | 73 | 74 | def _import_module(name): 75 | """Import module, returning the module after the last dot.""" 76 | __import__(name) 77 | return sys.modules[name] 78 | 79 | 80 | class _LazyDescr(object): 81 | 82 | def __init__(self, name): 83 | self.name = name 84 | 85 | def __get__(self, obj, tp): 86 | result = self._resolve() 87 | setattr(obj, self.name, result) 88 | # This is a bit ugly, but it avoids running this again. 89 | delattr(tp, self.name) 90 | return result 91 | 92 | 93 | class MovedModule(_LazyDescr): 94 | 95 | def __init__(self, name, old, new=None): 96 | super(MovedModule, self).__init__(name) 97 | if PY3: 98 | if new is None: 99 | new = name 100 | self.mod = new 101 | else: 102 | self.mod = old 103 | 104 | def _resolve(self): 105 | return _import_module(self.mod) 106 | 107 | 108 | class MovedAttribute(_LazyDescr): 109 | 110 | def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): 111 | super(MovedAttribute, self).__init__(name) 112 | if PY3: 113 | if new_mod is None: 114 | new_mod = name 115 | self.mod = new_mod 116 | if new_attr is None: 117 | if old_attr is None: 118 | new_attr = name 119 | else: 120 | new_attr = old_attr 121 | self.attr = new_attr 122 | else: 123 | self.mod = old_mod 124 | if old_attr is None: 125 | old_attr = name 126 | self.attr = old_attr 127 | 128 | def _resolve(self): 129 | module = _import_module(self.mod) 130 | return getattr(module, self.attr) 131 | 132 | 133 | 134 | class _MovedItems(types.ModuleType): 135 | """Lazy loading of moved objects""" 136 | 137 | 138 | _moved_attributes = [ 139 | MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), 140 | MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), 141 | MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), 142 | MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), 143 | MovedAttribute("map", "itertools", "builtins", "imap", "map"), 144 | MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), 145 | MovedAttribute("reload_module", "__builtin__", "imp", "reload"), 146 | MovedAttribute("reduce", "__builtin__", "functools"), 147 | MovedAttribute("StringIO", "StringIO", "io"), 148 | MovedAttribute("UserString", "UserString", "collections"), 149 | MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), 150 | MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), 151 | MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), 152 | 153 | MovedModule("builtins", "__builtin__"), 154 | MovedModule("configparser", "ConfigParser"), 155 | MovedModule("copyreg", "copy_reg"), 156 | MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), 157 | MovedModule("http_cookies", "Cookie", "http.cookies"), 158 | MovedModule("html_entities", "htmlentitydefs", "html.entities"), 159 | MovedModule("html_parser", "HTMLParser", "html.parser"), 160 | MovedModule("http_client", "httplib", "http.client"), 161 | MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), 162 | MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), 163 | MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), 164 | MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), 165 | MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), 166 | MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), 167 | MovedModule("cPickle", "cPickle", "pickle"), 168 | MovedModule("queue", "Queue"), 169 | MovedModule("reprlib", "repr"), 170 | MovedModule("socketserver", "SocketServer"), 171 | MovedModule("tkinter", "Tkinter"), 172 | MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), 173 | MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), 174 | MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), 175 | MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), 176 | MovedModule("tkinter_tix", "Tix", "tkinter.tix"), 177 | MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), 178 | MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), 179 | MovedModule("tkinter_colorchooser", "tkColorChooser", 180 | "tkinter.colorchooser"), 181 | MovedModule("tkinter_commondialog", "tkCommonDialog", 182 | "tkinter.commondialog"), 183 | MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), 184 | MovedModule("tkinter_font", "tkFont", "tkinter.font"), 185 | MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), 186 | MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", 187 | "tkinter.simpledialog"), 188 | MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), 189 | MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), 190 | MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), 191 | MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), 192 | MovedModule("winreg", "_winreg"), 193 | ] 194 | for attr in _moved_attributes: 195 | setattr(_MovedItems, attr.name, attr) 196 | del attr 197 | 198 | moves = sys.modules[__name__ + ".moves"] = _MovedItems(__name__ + ".moves") 199 | 200 | 201 | 202 | class Module_six_moves_urllib_parse(types.ModuleType): 203 | """Lazy loading of moved objects in six.moves.urllib_parse""" 204 | 205 | 206 | _urllib_parse_moved_attributes = [ 207 | MovedAttribute("ParseResult", "urlparse", "urllib.parse"), 208 | MovedAttribute("parse_qs", "urlparse", "urllib.parse"), 209 | MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), 210 | MovedAttribute("urldefrag", "urlparse", "urllib.parse"), 211 | MovedAttribute("urljoin", "urlparse", "urllib.parse"), 212 | MovedAttribute("urlparse", "urlparse", "urllib.parse"), 213 | MovedAttribute("urlsplit", "urlparse", "urllib.parse"), 214 | MovedAttribute("urlunparse", "urlparse", "urllib.parse"), 215 | MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), 216 | MovedAttribute("quote", "urllib", "urllib.parse"), 217 | MovedAttribute("quote_plus", "urllib", "urllib.parse"), 218 | MovedAttribute("unquote", "urllib", "urllib.parse"), 219 | MovedAttribute("unquote_plus", "urllib", "urllib.parse"), 220 | MovedAttribute("urlencode", "urllib", "urllib.parse"), 221 | ] 222 | for attr in _urllib_parse_moved_attributes: 223 | setattr(Module_six_moves_urllib_parse, attr.name, attr) 224 | del attr 225 | 226 | sys.modules[__name__ + ".moves.urllib_parse"] = Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse") 227 | sys.modules[__name__ + ".moves.urllib.parse"] = Module_six_moves_urllib_parse(__name__ + ".moves.urllib.parse") 228 | 229 | 230 | class Module_six_moves_urllib_error(types.ModuleType): 231 | """Lazy loading of moved objects in six.moves.urllib_error""" 232 | 233 | 234 | _urllib_error_moved_attributes = [ 235 | MovedAttribute("URLError", "urllib2", "urllib.error"), 236 | MovedAttribute("HTTPError", "urllib2", "urllib.error"), 237 | MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), 238 | ] 239 | for attr in _urllib_error_moved_attributes: 240 | setattr(Module_six_moves_urllib_error, attr.name, attr) 241 | del attr 242 | 243 | sys.modules[__name__ + ".moves.urllib_error"] = Module_six_moves_urllib_error(__name__ + ".moves.urllib_error") 244 | sys.modules[__name__ + ".moves.urllib.error"] = Module_six_moves_urllib_error(__name__ + ".moves.urllib.error") 245 | 246 | 247 | class Module_six_moves_urllib_request(types.ModuleType): 248 | """Lazy loading of moved objects in six.moves.urllib_request""" 249 | 250 | 251 | _urllib_request_moved_attributes = [ 252 | MovedAttribute("urlopen", "urllib2", "urllib.request"), 253 | MovedAttribute("install_opener", "urllib2", "urllib.request"), 254 | MovedAttribute("build_opener", "urllib2", "urllib.request"), 255 | MovedAttribute("pathname2url", "urllib", "urllib.request"), 256 | MovedAttribute("url2pathname", "urllib", "urllib.request"), 257 | MovedAttribute("getproxies", "urllib", "urllib.request"), 258 | MovedAttribute("Request", "urllib2", "urllib.request"), 259 | MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), 260 | MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), 261 | MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), 262 | MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), 263 | MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), 264 | MovedAttribute("BaseHandler", "urllib2", "urllib.request"), 265 | MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), 266 | MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), 267 | MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), 268 | MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), 269 | MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), 270 | MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), 271 | MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), 272 | MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), 273 | MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), 274 | MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), 275 | MovedAttribute("FileHandler", "urllib2", "urllib.request"), 276 | MovedAttribute("FTPHandler", "urllib2", "urllib.request"), 277 | MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), 278 | MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), 279 | MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), 280 | MovedAttribute("urlretrieve", "urllib", "urllib.request"), 281 | MovedAttribute("urlcleanup", "urllib", "urllib.request"), 282 | MovedAttribute("URLopener", "urllib", "urllib.request"), 283 | MovedAttribute("FancyURLopener", "urllib", "urllib.request"), 284 | ] 285 | for attr in _urllib_request_moved_attributes: 286 | setattr(Module_six_moves_urllib_request, attr.name, attr) 287 | del attr 288 | 289 | sys.modules[__name__ + ".moves.urllib_request"] = Module_six_moves_urllib_request(__name__ + ".moves.urllib_request") 290 | sys.modules[__name__ + ".moves.urllib.request"] = Module_six_moves_urllib_request(__name__ + ".moves.urllib.request") 291 | 292 | 293 | class Module_six_moves_urllib_response(types.ModuleType): 294 | """Lazy loading of moved objects in six.moves.urllib_response""" 295 | 296 | 297 | _urllib_response_moved_attributes = [ 298 | MovedAttribute("addbase", "urllib", "urllib.response"), 299 | MovedAttribute("addclosehook", "urllib", "urllib.response"), 300 | MovedAttribute("addinfo", "urllib", "urllib.response"), 301 | MovedAttribute("addinfourl", "urllib", "urllib.response"), 302 | ] 303 | for attr in _urllib_response_moved_attributes: 304 | setattr(Module_six_moves_urllib_response, attr.name, attr) 305 | del attr 306 | 307 | sys.modules[__name__ + ".moves.urllib_response"] = Module_six_moves_urllib_response(__name__ + ".moves.urllib_response") 308 | sys.modules[__name__ + ".moves.urllib.response"] = Module_six_moves_urllib_response(__name__ + ".moves.urllib.response") 309 | 310 | 311 | class Module_six_moves_urllib_robotparser(types.ModuleType): 312 | """Lazy loading of moved objects in six.moves.urllib_robotparser""" 313 | 314 | 315 | _urllib_robotparser_moved_attributes = [ 316 | MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), 317 | ] 318 | for attr in _urllib_robotparser_moved_attributes: 319 | setattr(Module_six_moves_urllib_robotparser, attr.name, attr) 320 | del attr 321 | 322 | sys.modules[__name__ + ".moves.urllib_robotparser"] = Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib_robotparser") 323 | sys.modules[__name__ + ".moves.urllib.robotparser"] = Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser") 324 | 325 | 326 | class Module_six_moves_urllib(types.ModuleType): 327 | """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" 328 | parse = sys.modules[__name__ + ".moves.urllib_parse"] 329 | error = sys.modules[__name__ + ".moves.urllib_error"] 330 | request = sys.modules[__name__ + ".moves.urllib_request"] 331 | response = sys.modules[__name__ + ".moves.urllib_response"] 332 | robotparser = sys.modules[__name__ + ".moves.urllib_robotparser"] 333 | 334 | 335 | sys.modules[__name__ + ".moves.urllib"] = Module_six_moves_urllib(__name__ + ".moves.urllib") 336 | 337 | 338 | def add_move(move): 339 | """Add an item to six.moves.""" 340 | setattr(_MovedItems, move.name, move) 341 | 342 | 343 | def remove_move(name): 344 | """Remove item from six.moves.""" 345 | try: 346 | delattr(_MovedItems, name) 347 | except AttributeError: 348 | try: 349 | del moves.__dict__[name] 350 | except KeyError: 351 | raise AttributeError("no such move, %r" % (name,)) 352 | 353 | 354 | if PY3: 355 | _meth_func = "__func__" 356 | _meth_self = "__self__" 357 | 358 | _func_closure = "__closure__" 359 | _func_code = "__code__" 360 | _func_defaults = "__defaults__" 361 | _func_globals = "__globals__" 362 | 363 | _iterkeys = "keys" 364 | _itervalues = "values" 365 | _iteritems = "items" 366 | _iterlists = "lists" 367 | else: 368 | _meth_func = "im_func" 369 | _meth_self = "im_self" 370 | 371 | _func_closure = "func_closure" 372 | _func_code = "func_code" 373 | _func_defaults = "func_defaults" 374 | _func_globals = "func_globals" 375 | 376 | _iterkeys = "iterkeys" 377 | _itervalues = "itervalues" 378 | _iteritems = "iteritems" 379 | _iterlists = "iterlists" 380 | 381 | 382 | try: 383 | advance_iterator = next 384 | except NameError: 385 | def advance_iterator(it): 386 | return it.next() 387 | next = advance_iterator 388 | 389 | 390 | try: 391 | callable = callable 392 | except NameError: 393 | def callable(obj): 394 | return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) 395 | 396 | 397 | if PY3: 398 | def get_unbound_function(unbound): 399 | return unbound 400 | 401 | create_bound_method = types.MethodType 402 | 403 | Iterator = object 404 | else: 405 | def get_unbound_function(unbound): 406 | return unbound.im_func 407 | 408 | def create_bound_method(func, obj): 409 | return types.MethodType(func, obj, obj.__class__) 410 | 411 | class Iterator(object): 412 | 413 | def next(self): 414 | return type(self).__next__(self) 415 | 416 | callable = callable 417 | _add_doc(get_unbound_function, 418 | """Get the function out of a possibly unbound function""") 419 | 420 | 421 | get_method_function = operator.attrgetter(_meth_func) 422 | get_method_self = operator.attrgetter(_meth_self) 423 | get_function_closure = operator.attrgetter(_func_closure) 424 | get_function_code = operator.attrgetter(_func_code) 425 | get_function_defaults = operator.attrgetter(_func_defaults) 426 | get_function_globals = operator.attrgetter(_func_globals) 427 | 428 | 429 | def iterkeys(d, **kw): 430 | """Return an iterator over the keys of a dictionary.""" 431 | return iter(getattr(d, _iterkeys)(**kw)) 432 | 433 | def itervalues(d, **kw): 434 | """Return an iterator over the values of a dictionary.""" 435 | return iter(getattr(d, _itervalues)(**kw)) 436 | 437 | def iteritems(d, **kw): 438 | """Return an iterator over the (key, value) pairs of a dictionary.""" 439 | return iter(getattr(d, _iteritems)(**kw)) 440 | 441 | def iterlists(d, **kw): 442 | """Return an iterator over the (key, [values]) pairs of a dictionary.""" 443 | return iter(getattr(d, _iterlists)(**kw)) 444 | 445 | 446 | if PY3: 447 | def b(s): 448 | return s.encode("latin-1") 449 | def u(s): 450 | return s 451 | unichr = chr 452 | if sys.version_info[1] <= 1: 453 | def int2byte(i): 454 | return bytes((i,)) 455 | else: 456 | # This is about 2x faster than the implementation above on 3.2+ 457 | int2byte = operator.methodcaller("to_bytes", 1, "big") 458 | byte2int = operator.itemgetter(0) 459 | indexbytes = operator.getitem 460 | iterbytes = iter 461 | import io 462 | StringIO = io.StringIO 463 | BytesIO = io.BytesIO 464 | else: 465 | def b(s): 466 | return s 467 | def u(s): 468 | return unicode(s, "unicode_escape") 469 | unichr = unichr 470 | int2byte = chr 471 | def byte2int(bs): 472 | return ord(bs[0]) 473 | def indexbytes(buf, i): 474 | return ord(buf[i]) 475 | def iterbytes(buf): 476 | return (ord(byte) for byte in buf) 477 | import StringIO 478 | StringIO = BytesIO = StringIO.StringIO 479 | _add_doc(b, """Byte literal""") 480 | _add_doc(u, """Text literal""") 481 | 482 | 483 | if PY3: 484 | import builtins 485 | exec_ = getattr(builtins, "exec") 486 | 487 | 488 | def reraise(tp, value, tb=None): 489 | if value.__traceback__ is not tb: 490 | raise value.with_traceback(tb) 491 | raise value 492 | 493 | 494 | print_ = getattr(builtins, "print") 495 | del builtins 496 | 497 | else: 498 | def exec_(_code_, _globs_=None, _locs_=None): 499 | """Execute code in a namespace.""" 500 | if _globs_ is None: 501 | frame = sys._getframe(1) 502 | _globs_ = frame.f_globals 503 | if _locs_ is None: 504 | _locs_ = frame.f_locals 505 | del frame 506 | elif _locs_ is None: 507 | _locs_ = _globs_ 508 | exec("""exec _code_ in _globs_, _locs_""") 509 | 510 | 511 | exec_("""def reraise(tp, value, tb=None): 512 | raise tp, value, tb 513 | """) 514 | 515 | 516 | def print_(*args, **kwargs): 517 | """The new-style print function.""" 518 | fp = kwargs.pop("file", sys.stdout) 519 | if fp is None: 520 | return 521 | def write(data): 522 | if not isinstance(data, basestring): 523 | data = str(data) 524 | fp.write(data) 525 | want_unicode = False 526 | sep = kwargs.pop("sep", None) 527 | if sep is not None: 528 | if isinstance(sep, unicode): 529 | want_unicode = True 530 | elif not isinstance(sep, str): 531 | raise TypeError("sep must be None or a string") 532 | end = kwargs.pop("end", None) 533 | if end is not None: 534 | if isinstance(end, unicode): 535 | want_unicode = True 536 | elif not isinstance(end, str): 537 | raise TypeError("end must be None or a string") 538 | if kwargs: 539 | raise TypeError("invalid keyword arguments to print()") 540 | if not want_unicode: 541 | for arg in args: 542 | if isinstance(arg, unicode): 543 | want_unicode = True 544 | break 545 | if want_unicode: 546 | newline = unicode("\n") 547 | space = unicode(" ") 548 | else: 549 | newline = "\n" 550 | space = " " 551 | if sep is None: 552 | sep = space 553 | if end is None: 554 | end = newline 555 | for i, arg in enumerate(args): 556 | if i: 557 | write(sep) 558 | write(arg) 559 | write(end) 560 | 561 | _add_doc(reraise, """Reraise an exception.""") 562 | 563 | 564 | def with_metaclass(meta, *bases): 565 | """Create a base class with a metaclass.""" 566 | return meta("NewBase", bases, {}) 567 | 568 | def add_metaclass(metaclass): 569 | """Class decorator for creating a class with a metaclass.""" 570 | def wrapper(cls): 571 | orig_vars = cls.__dict__.copy() 572 | orig_vars.pop('__dict__', None) 573 | orig_vars.pop('__weakref__', None) 574 | for slots_var in orig_vars.get('__slots__', ()): 575 | orig_vars.pop(slots_var) 576 | return metaclass(cls.__name__, cls.__bases__, orig_vars) 577 | return wrapper 578 | --------------------------------------------------------------------------------