fixgen.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. #!/usr/bin/env python3
  2. import sys
  3. import os
  4. import re
  5. from urllib.request import urlopen
  6. # How to use:
  7. # go get github.com/hooklift/gowsdl/...
  8. # ./fixgen.py [onvif profile file name without .wsdl]
  9. if len(sys.argv) != 2:
  10. print("Usage:\n./fixgen.py [path to profile file without .wsdl]")
  11. exit(1)
  12. go_package = os.path.basename(sys.argv[1])
  13. go_src = os.path.basename(sys.argv[1]) + '.go'
  14. wsdl_file = sys.argv[1] + '.wsdl'
  15. with open(wsdl_file, 'r') as file:
  16. wsdl = file.read()
  17. r = re.findall(r'targetNamespace="(http://www.onvif.org/.+)"', wsdl)
  18. if len(r):
  19. targetNamespace = r[0]
  20. os.system('gowsdl -o ' + go_src + ' -p ' + go_package + ' ' + wsdl_file + ' | grep -v expected')
  21. with open(go_package + '/' + go_src, 'r') as file:
  22. data = file.read()
  23. data = data.replace('// Code generated by gowsdl DO NOT EDIT.', '')
  24. print(' - Replace import')
  25. data = data.replace('"github.com/hooklift/gowsdl/soap"',
  26. '"github.com/videonext/onvif/soap"')
  27. ########################################################################
  28. print(' - Working around some bugs in the gowsdl')
  29. data = data.replace('interface{}', '')
  30. data = data.replace('TLS1.0 bool', 'TLS1_0 bool')
  31. data = data.replace('TLS1.1 bool', 'TLS1_1 bool')
  32. data = data.replace('TLS1.2 bool', 'TLS1_2 bool')
  33. data = data.replace('X.509Token bool', 'X_509Token bool')
  34. ########################################################################
  35. print(' - Adding wsdl\'s namespace and method name to the SOAP action')
  36. data = re.sub(r'(?s)func \(service (\*\w+\)) (\w+)Context\s*\(ctx context.Context, request (\*\w+\)) \((\*\w+), error\)(.+?)service\.client\.CallContext\(ctx, "(\'\')",',
  37. r'func (service \1 \2Context(ctx context.Context, request \3 (\4, error)\5service.client.CallContext(ctx, "' + targetNamespace + '/' + r'\2",', data)
  38. ########################################################################
  39. print(' - Patching object, CallContext and New* functions: add xaddr field/arg')
  40. data = re.sub(r'(?s)type\s+(\w+?)\s+struct\s+\{\s+?client\s+\*soap\.Client',
  41. r'type \1 struct {\nclient *soap.Client\nxaddr string\n', data)
  42. data = data.replace('service.client.CallContext(ctx,', 'service.client.CallContext(ctx, service.xaddr,')
  43. data = re.sub(r'(?s)func New(.+?)\(client \*soap.Client\) (.+?) \{.+?return \&(\w+)\{.+?}',
  44. r'func New\1(client *soap.Client, xaddr string) \2 {\n return &\3{\nclient: client,\nxaddr: xaddr,\n}', data)
  45. ########################################################################
  46. print(' - Fixing namespaces in the xsd types')
  47. type_map = {}
  48. xsds = ['http://www.onvif.org/ver10/schema/common.xsd',
  49. 'http://www.onvif.org/ver10/schema/onvif.xsd',
  50. 'http://www.w3.org/2001/xml.xsd',
  51. 'http://www.onvif.org/ver10/schema/metadatastream.xsd',
  52. 'http://www.onvif.org/ver10/pacs/types.xsd',
  53. 'http://www.onvif.org/ver20/analytics/rules.xsd',
  54. 'http://www.onvif.org/ver20/analytics/radiometry.xsd']
  55. for xsd in xsds:
  56. xsd_data = urlopen(xsd).read().decode('utf-8')
  57. r = re.findall(r'targetNamespace="(http://.+?)"', xsd_data)
  58. if len(r):
  59. ns = r[0]
  60. else:
  61. raise Exception('No namespace in the ' + xsd)
  62. r = re.findall(r'\<xs:\w+Type name="(\w+)">', xsd_data)
  63. for t in r:
  64. if not t[0].isupper():
  65. t = t.title()
  66. if t in type_map:
  67. continue
  68. type_map[t] = ns
  69. # reading used types from onvif.xsd
  70. onvif_xsd_data = urlopen('http://www.onvif.org/ver10/schema/onvif.xsd').read().decode('utf-8')
  71. r = re.findall(r'type="xs:(.+?)"', onvif_xsd_data)
  72. for t in r:
  73. if not t[0].isupper():
  74. t = t.title()
  75. if t in type_map:
  76. continue
  77. type_map[t] = 'http://www.onvif.org/ver10/schema'
  78. # reading used types from common.xsd
  79. common_xsd_data = urlopen('http://www.onvif.org/ver10/schema/common.xsd').read().decode('utf-8')
  80. r = re.findall(r'type="xs:(.+?)"', common_xsd_data)
  81. for t in r:
  82. if not t[0].isupper():
  83. t = t.title()
  84. if t in type_map:
  85. continue
  86. type_map[t] = 'http://www.onvif.org/ver10/schema'
  87. # reading types from wsdl
  88. r = re.findall(r'\<xs:\w+Type name="(\w+)">', wsdl)
  89. for t in r:
  90. if not t[0].isupper():
  91. t = t.title()
  92. if t in type_map:
  93. continue
  94. type_map[t] = targetNamespace
  95. # some other types
  96. type_map['String'] = 'http://www.onvif.org/ver10/schema'
  97. type_map['string'] = 'http://www.onvif.org/ver10/schema'
  98. type_map['time.Time'] = 'http://www.onvif.org/ver10/schema'
  99. type_map['byte'] = 'http://www.onvif.org/ver10/schema'
  100. type_map['int'] = 'http://www.onvif.org/ver10/schema'
  101. type_map['uint'] = 'http://www.onvif.org/ver10/schema'
  102. type_map['int8'] = 'http://www.onvif.org/ver10/schema'
  103. type_map['uint8'] = 'http://www.onvif.org/ver10/schema'
  104. type_map['int16'] = 'http://www.onvif.org/ver10/schema'
  105. type_map['uint16'] = 'http://www.onvif.org/ver10/schema'
  106. type_map['int32'] = 'http://www.onvif.org/ver10/schema'
  107. type_map['uint32'] = 'http://www.onvif.org/ver10/schema'
  108. type_map['int64'] = 'http://www.onvif.org/ver10/schema'
  109. type_map['uint64'] = 'http://www.onvif.org/ver10/schema'
  110. type_map['float32'] = 'http://www.onvif.org/ver10/schema'
  111. type_map['float64'] = 'http://www.onvif.org/ver10/schema'
  112. type_map['bool'] = 'http://www.onvif.org/ver10/schema'
  113. type_map['[]string'] = 'http://www.onvif.org/ver10/schema'
  114. type_map['ReferenceToken'] = 'http://www.onvif.org/ver10/schema'
  115. type_map['time.Time'] = 'http://www.onvif.org/ver10/schema'
  116. type_map['NonNegativeInteger'] = 'http://www.onvif.org/ver10/schema'
  117. if type_map['Anyuri'] != None:
  118. type_map['AnyURI'] = type_map['Anyuri']
  119. # print(type_map)
  120. for k, v in type_map.items():
  121. ns = v
  122. # # check if type used in the wsdl we are processing
  123. r = re.findall(r'type="\w+:' + re.escape(k), wsdl)
  124. # if len(r) and (re.search(r'complexType name="' + k + '"', common_xsd_data) or re.search(r'complexType name="' + k + '"', onvif_xsd_data)):
  125. # ns = targetNamespace
  126. if len(r):
  127. ns = targetNamespace
  128. data = re.sub(r"(?s)(\w+)\s+\*" + re.escape(k) + r"\s+`xml:\"\1(.*?)\"`",
  129. r"\1 *" + k + r' `xml:"' + ns + r' \1\2"`',
  130. data)
  131. data = re.sub(r"(?s)(\w+)\s+\[\]\*" + re.escape(k) + r"\s+`xml:\"\1(.*?)\"`",
  132. r"\1 []*" + k + r' `xml:"' + ns + r' \1\2"`',
  133. data)
  134. data = re.sub(r"(?s)(\w+)\s+\[\]" + re.escape(k) + r"\s+`xml:\"\1(.*?)\"`",
  135. r"\1 []" + k + r' `xml:"' + ns + r' \1\2"`',
  136. data)
  137. data = re.sub(r"(?s)(\w+)\s+" + re.escape(k) + r"\s+`xml:\"\1(.*?)\"`",
  138. r"\1 " + k + r' `xml:"' + ns + r' \1\2"`',
  139. data)
  140. ########################################################################
  141. with open(go_package + '/' + go_src, 'w') as file:
  142. file.write(data)
  143. os.system("gofmt -w " + go_package + '/' + go_src)
  144. with open(go_package + '/' + go_src, 'r') as file:
  145. data = file.read()
  146. print(' - Adding missed simple types')
  147. data += "\ntype AnyURI string\n"
  148. data += "type Duration string\n"
  149. data += "type QName string\n"
  150. data += "type NCName string\n"
  151. data += "type NonNegativeInteger int64\n"
  152. data += "type PositiveInteger int64\n"
  153. data += "type NonPositiveInteger int64\n"
  154. data += "type AnySimpleType string\n"
  155. data += "type String string\n"
  156. ########################################################################
  157. print(' - Removing unused types')
  158. for k, v in type_map.items():
  159. r0 = re.findall(r"(\w+)\s+" + re.escape(k) + r"\s+`xml:\"", data)
  160. r1 = re.findall(r"(\w+)\s+\*" + re.escape(k) + r"\s+`xml:\"", data)
  161. r2 = re.findall(r"(\w+)\s+\[\]" + re.escape(k) + r"\s+`xml:\"", data)
  162. r3 = re.findall(r"(\w+)\s+\[\]\*" + re.escape(k) + r"\s+`xml:\"", data)
  163. r4 = re.findall(r"\*" + re.escape(k), data)
  164. r5 = re.findall(r"\[\]" + re.escape(k), data)
  165. r6 = re.findall(re.escape(k) + r' = ', data)
  166. r7 = re.findall(r'type \w+ ' + re.escape(k), data)
  167. if len(r0) == 0 and len(r1) == 0 and len(r2) == 0 and len(r3) == 0 and len(r4) == 0 and len(r5) == 0 and len(r6) == 0 and len(r7) == 0:
  168. regex = re.compile(r"(?s)type\s+" + re.escape(k) + r"\s+struct\s+\{(.+?)^\}", re.MULTILINE)
  169. data = re.sub(regex, '', data)
  170. regex = re.compile(r"(?s)type\s+" + re.escape(k) + r"\s+\w+\n", re.MULTILINE)
  171. data = re.sub(regex, '', data)
  172. r = re.findall(r"type (\w+) ", data)
  173. for t in r:
  174. r0 = re.findall(r"(\w+)\s+" + re.escape(t) + r"\s+`xml:\"", data)
  175. r1 = re.findall(r"(\w+)\s+\*" + re.escape(t) + r"\s+`xml:\"", data)
  176. r2 = re.findall(r"(\w+)\s+\[\]" + re.escape(t) + r"\s+`xml:\"", data)
  177. r3 = re.findall(r"(\w+)\s+\[\]\*" + re.escape(t) + r"\s+`xml:\"", data)
  178. r4 = re.findall(r"\*" + re.escape(t), data)
  179. r5 = re.findall(r"\[\]" + re.escape(t), data)
  180. r6 = re.findall(re.escape(t) + r' = ', data)
  181. r7 = re.findall(r'type \w+ ' + re.escape(t), data)
  182. if len(r0) == 0 and len(r1) == 0 and len(r2) == 0 and len(r3) == 0 and len(r4) == 0 and len(r5) == 0 and len(r6) == 0 and len(r7) == 0:
  183. regex = re.compile(r"(?s)type\s+" + re.escape(t) + r"\s+struct\s+\{(.+?)^\}", re.MULTILINE)
  184. data = re.sub(regex, '', data)
  185. regex = re.compile(r"(?s)type\s+" + re.escape(t) + r"\s+\w+\n", re.MULTILINE)
  186. data = re.sub(regex, '', data)
  187. ########################################################################
  188. print(' - Removing pointers for simple types')
  189. data = data.replace('*AnyURI', 'AnyURI')
  190. data = data.replace('*Duration', 'Duration')
  191. data = data.replace('*QName', 'QName')
  192. data = data.replace('*NonNegativeInteger', 'NonNegativeInteger')
  193. data = data.replace('*PositiveInteger', 'PositiveInteger')
  194. data = data.replace('*NonPositiveInteger', 'NonPositiveInteger')
  195. data = data.replace('*AnySimpleType', 'AnySimpleType')
  196. data = data.replace('*Description', 'Description')
  197. data = data.replace('*Name `xml', 'Name `xml')
  198. data = data.replace('*string `xml', 'string `xml')
  199. data = data.replace('*String `xml', 'String `xml')
  200. data = data.replace('*int32 `xml', 'int32 `xml')
  201. data = data.replace('*float32 `xml', 'float32 `xml')
  202. data = data.replace('*bool `xml', 'bool `xml')
  203. data = data.replace('*time.Time `xml', 'string `xml')
  204. data = data.replace('time.Time `xml', 'string `xml')
  205. data = data.replace('[]*', '[]')
  206. data = data.replace('*NCName', 'NCName')
  207. ########################################################################
  208. print(' - Removing duplicated types')
  209. data = re.sub(r'type \b(\w+)\s+\1\b', '', data)
  210. data = data.replace('type IntList IntAttrList', '')
  211. data = data.replace('type FloatList FloatAttrList', '')
  212. data = data.replace('type Capabilities DeviceServiceCapabilities', '')
  213. data = data.replace('type FaultcodeEnum *QName', 'type FaultcodeEnum QName')
  214. data = data.replace('type FaultCodesType *QName', 'type FaultCodesType QName')
  215. data = data.replace('type RelationshipType *AnyURI', 'type RelationshipType AnyURI')
  216. data = re.sub(r'(?s)type QueryExpressionType struct \{\n\s+XMLName xml\.Name `xml:"http://docs\.oasis-open\.org/wsn/b-2 ProducerProperties(.+?)\}',
  217. r'// Removed QueryExpressionType', data)
  218. r = re.findall(r'(?s)(type Capabilities struct(.+?)\})', data)
  219. if len(r) == 2:
  220. data = data.replace(r[1][0], '/* Removed ' + r[1][0] + ' Removed*/')
  221. ########################################################################
  222. print(' - Removing pointers to xml data types')
  223. data = re.sub(r"(\w+)\s+\*(\w+)\s+`xml:\"(.*?)\"`",
  224. r'\1 \2 `xml:"\3"`',
  225. data)
  226. data = re.sub(r"(\w+)\s+\[\]\*(\w+)\s+`xml:\"(.*?)\"`",
  227. r'\1 []\2 `xml:"\3"`',
  228. data)
  229. # keep some of them to prevent recursion
  230. data = data.replace('Extension NetworkZeroConfigurationExtension `xml:"', 'Extension *NetworkZeroConfigurationExtension `xml:"')
  231. data = data.replace('Tunnel Transport `xml:"', 'Tunnel *Transport `xml:"')
  232. data = data.replace('Subcode Subcode `xml:"', 'Subcode *Subcode `xml:"')
  233. data = data.replace('*Ref', 'Ref')
  234. ########################################################################
  235. # add comments to exported types
  236. regex = re.compile(r'^type\s+(.+?)\s+', re.MULTILINE)
  237. data = re.sub(regex, r'// \1 type\ntype \1 ', data)
  238. # add comments to exported constants
  239. regex = re.compile(r'(\w+) (\w+) = "(.+?)"', re.MULTILINE)
  240. data = re.sub(regex, r'// \1 const\n\1 \2 = "\3" ', data)
  241. # remove ns from *Response* types
  242. regex = re.compile(r'(?s)^(type \w+Response\w+ struct \{.+?\})', re.MULTILINE)
  243. r = re.findall(regex, data)
  244. regex = re.compile(r'(?s)^(type \w+Response struct \{.+?\})', re.MULTILINE)
  245. r += re.findall(regex, data)
  246. for block in r:
  247. block2 = re.sub(r'xml:"http.+? ', 'xml:"', block)
  248. data = data.replace(block, block2)
  249. with open(go_package + '/' + go_src, 'w') as file:
  250. file.write(data)
  251. os.system("gofmt -w " + go_package + '/' + go_src)
  252. print('Done')