The little things give you away... A collection of various small helper stuff
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 

115 lignes
3.8 KiB

  1. #!/usr/bin/env python3
  2. import html
  3. import http.client
  4. import os
  5. import shlex
  6. import ssl
  7. import sys
  8. import urllib.parse
  9. # Arguments
  10. i = 1
  11. withListUrls = False
  12. listUrlsFD = None
  13. startMarker = None
  14. format = '{url}'
  15. args = []
  16. while i < len(sys.argv):
  17. arg = sys.argv[i]
  18. if arg == '--help':
  19. print('azure-storage-list [options] CONTAINERURL', file = sys.stderr)
  20. print('', file = sys.stderr)
  21. print('Options:', file = sys.stderr)
  22. print(f' --format FORMAT Modify the output format; FORMAT defaults to {format!r}; available fields: name, url, and all fields returned by Azure (e.g. Content-Length, Last-Modified)', file = sys.stderr)
  23. print( ' --marker MARKER Start with a marker instead of from the beginning', file = sys.stderr)
  24. print( ' --with-list-urls Enables printing the list URLs retrieved to FD 3', file = sys.stderr)
  25. sys.exit(1)
  26. elif arg == '--with-list-urls':
  27. withListUrls = True
  28. try:
  29. listUrlsFD = os.fdopen(3, 'w')
  30. except OSError:
  31. print('Error: FD 3 not open', file = sys.stderr)
  32. sys.exit(1)
  33. elif arg == '--marker':
  34. startMarker = sys.argv[i + 1]
  35. i += 1
  36. elif arg == '--format':
  37. format = sys.argv[i + 1]
  38. i += 1
  39. else:
  40. args.append(arg)
  41. i += 1
  42. assert len(args) == 1, 'Need one argument: container URL'
  43. baseUrl = args[0]
  44. assert baseUrl.startswith('http://') or baseUrl.startswith('https://'), 'Argument does not look like an HTTP URL'
  45. if '/' not in baseUrl.split('://', 1)[1] or not baseUrl.endswith('/'):
  46. baseUrl = f'{baseUrl}/'
  47. hostname = baseUrl.split('://', 1)[1].split('/', 1)[0]
  48. conn = http.client.HTTPSConnection(hostname, context = ssl._create_unverified_context())
  49. params = {'restype': 'container', 'comp': 'list'}
  50. if startMarker is not None:
  51. params['marker'] = startMarker
  52. while True:
  53. queryString = urllib.parse.urlencode(params)
  54. url = f'{baseUrl}?{queryString}'
  55. if withListUrls:
  56. print(f'{url}', file = listUrlsFD)
  57. conn.request('GET', url[url.index('/', 8):])
  58. resp = conn.getresponse()
  59. body = resp.read()
  60. if not body.startswith(b'\xef\xbb\xbf<?xml version="1.0" encoding="utf-8"?><EnumerationResults ContainerName="'):
  61. raise RuntimeError(f'Invalid body: {body[:200]}...')
  62. if b'<Marker>' not in body[:200] and 'marker' in params:
  63. raise RuntimeError('Marker loop (no marker in response despite providing one)')
  64. # No risk, no fun!
  65. blobs = body.split(b'<Blob>')
  66. assert all(blob.startswith(b'<Name>') for blob in blobs[1:])
  67. assert all(blob.endswith(b'</Blob>') for blob in blobs[1:-1])
  68. assert b'</Blobs>' in blobs[-1] and blobs[-1].endswith(b'</EnumerationResults>')
  69. blobs[-1], ending = blobs[-1].split(b'</Blobs>')
  70. assert b'<NextMarker' in ending
  71. for blob in blobs[1:]:
  72. name = html.unescape(blob[6 : blob.index(b'</Name>')].decode('utf-8')) # 6 = len(b'<Name>')
  73. url = f'{baseUrl}{urllib.parse.quote(name)}'
  74. tags = blob.split(b'>')
  75. assert tags[-1] == b''
  76. assert tags[-2] == b'</Blob'
  77. assert tags[-3] == b'</Properties'
  78. assert b'<Properties' in tags
  79. openTags = [] # Current open tag hierarchy
  80. fields = {}
  81. for tag in tags[:-3]:
  82. if tag == b'<Properties':
  83. continue
  84. if tag.endswith(b' /'): # Self-closing tag without a value
  85. continue
  86. if tag.startswith(b'<'):
  87. openTags.append(tag[1:])
  88. continue
  89. assert openTags
  90. if tag.endswith(b'</' + openTags[-1]):
  91. fields[b'>'.join(openTags).decode('utf-8')] = html.unescape(tag[:-(len(openTags[-1]) + 2)].decode('utf-8'))
  92. openTags.pop()
  93. continue
  94. assert False
  95. try:
  96. print(format.format(**fields, name = name, url = url))
  97. except BrokenPipeError:
  98. sys.exit(0)
  99. if b'<NextMarker />' in ending:
  100. break
  101. nextMarkerStart = ending.index(b'<NextMarker>')
  102. nextMarker = ending[nextMarkerStart + 12 : ending.index(b'</NextMarker', nextMarkerStart)]
  103. if 'marker' in params and params['marker'] == nextMarker:
  104. raise RuntimeError('Marker loop (same NextMarker as previous marker)')
  105. params['marker'] = nextMarker.decode('utf-8')