/[pcsclite]/website/matrix.py
ViewVC logotype

Contents of /website/matrix.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5140 - (show annotations) (download) (as text)
Mon Aug 16 08:02:26 2010 UTC (2 years, 10 months ago) by rousseau
File MIME type: text/x-python
File size: 14999 byte(s)
Define supported, shouldwork and unsupported colors in matrix.css
instead of hardcoding them using bgcolor="" 
1 #!/usr/bin/env python
2
3 # matrix.py generate a matrix of all readers characteristics
4 # Copyright (C) 2009 Ludovic Rousseau
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along
17 # with this program; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 import glob
21 import os
22 import ConfigParser
23 import pprint
24 import templayer
25 import time
26 import re
27
28 pp = pprint.PrettyPrinter(indent=4)
29
30 html_escape_table = {
31 "&": "&",
32 '"': """,
33 "'": "'",
34 ">": ">",
35 "<": "&lt;",
36 }
37
38
39 def html_escape(text):
40 """Produce entities within text."""
41 L = []
42 for c in text:
43 L.append(html_escape_table.get(c, c))
44 return "".join(L)
45
46
47 def parse_reader(path, reader):
48 """
49 parse a reader CCID descriptor and return a dictionnary
50 """
51 reader_dict = {}
52 reader_file = open(path + reader)
53 for line in reader_file.readlines():
54 line = line[0:-1]
55 l = line.strip(" ").split(':')
56 if (len(l) > 1):
57 reader_dict[l[0]] = l[1].strip(" ")
58 reader_file.close()
59 return reader_dict
60
61
62 def parse_all(path, reader_list):
63 """
64 parse each reader from list
65 return a dictionnary
66 """
67 readers = {}
68 for reader in reader_list:
69 p = parse_reader(path, reader)
70 readers[reader] = p
71
72 return readers
73
74
75 def parse_ini(path, section):
76 """
77 parse a foobar.ini file to extract all informations
78 """
79 config = ConfigParser.ConfigParser()
80 # do not use the default case insensitive transform for key value
81 config.optionxform = str
82 config.read(section + ".ini")
83 reader_list = config.sections()
84
85 readers = parse_all(path, reader_list)
86 for r in readers.keys():
87 readers[r]['section'] = section
88 readers[r]['iManufacturer'] = html_escape(readers[r]['iManufacturer'])
89 for o in config.options(r):
90 if o == 'features':
91 # for the features we use a list
92 readers[r][o] = [config.get(r, o)]
93 else:
94 readers[r][o] = config.get(r, o)
95
96 bPINSupport = int(readers[r]['bPINSupport'], 16)
97 if bPINSupport != 0 and not 'features' in readers[r]:
98 readers[r]['features'] = list()
99 if bPINSupport & 1:
100 readers[r]['features'].append("PIN Verification")
101 if bPINSupport & 2:
102 readers[r]['features'].append("PIN Modification")
103 if int(readers[r]['dwFeatures'], 16) & 0x0800:
104 readers[r].setdefault('features', ["ICCD"])
105
106 #pp.pprint(readers["GemPCPinpad.txt"])
107 return readers
108
109
110 def check_list(path, reader_list):
111 """
112 Check that all .txt files are listed
113 """
114 cwd = os.getcwd()
115 os.chdir(path)
116 real_list = glob.glob("*.txt")
117 os.chdir(cwd)
118
119 # check that each reader file is listed
120 #print real_list
121 for r in reader_list:
122 #print "remove ", r
123 try:
124 real_list.remove(r)
125 except:
126 print "reader %s not yet listed" % r
127
128 # also remove the non-reader supported_readers.txt file
129 real_list.remove("supported_readers.txt")
130
131 # some USB descriptor are not listed in readers.txt?
132 if len(real_list) > 0:
133 print "Reader(s) not listed in any .ini file:"
134 print "\n".join(real_list)
135 print ""
136
137
138 def check_supported(path, all_readers):
139 """
140 Check that all .txt files are mentionned in supported_readers.txt
141 """
142 supported_readers = file(path + "supported_readers.txt").readlines()
143 # convert in a long string and in uppercase
144 s = "".join(supported_readers).upper()
145
146 unlisted = list()
147 for r in all_readers.keys():
148 pattern = "%s:%s" % (all_readers[r]['idVendor'], all_readers[r]['idProduct'])
149 if not pattern.upper() in s:
150 unlisted.append(pattern + " " + r)
151
152 if len(unlisted) > 0:
153 print "Reader(s) not in supported_readers.txt"
154 print "\n".join(unlisted)
155 print ""
156 #pp.pprint(supported_readers)
157
158
159 def get_driver_version(readers):
160 """
161 set the 'release' field for each reader
162 """
163 changelog = get_changelog()
164
165 for reader in readers.keys():
166 rev = get_driver_revision(reader, changelog)
167 readers[reader]['release'] = driver_revision_to_version(rev)
168
169
170 def driver_revision_to_version(rev):
171 """
172 convert a SVN revision in the first release containing this revision
173 """
174 history = [
175 #[ SVN revision, CCID release ]
176 [273, "0.1.0"],
177 [342, "0.2.0"],
178 [423, "0.3.0"],
179 [467, "0.3.1"],
180 [552, "0.3.2"],
181 [697, "0.4.0"],
182 [703, "0.4.1"],
183 [1015, "0.9.0"],
184 [1018, "0.9.1"],
185 [1186, "0.9.2"],
186 [1400, "0.9.3"],
187 [1761, "0.9.4"],
188 [1911, "1.0.0"],
189 [2020, "1.0.1"],
190 [2135, "1.1.0"],
191 [2345, "1.2.0"],
192 [2363, "1.2.1"],
193 [2522, "1.3.0"],
194 [2692, "1.3.1"],
195 [2755, "1.3.2"],
196 [2796, "1.3.3"],
197 [2809, "1.3.4"],
198 [2842, "1.3.5"],
199 [2924, "1.3.6"],
200 [2985, "1.3.7"],
201 [3033, "1.3.8"],
202 [3208, "1.3.9"],
203 [3338, "1.3.10"],
204 [4347, "1.3.11"],
205 [4931, "1.3.12"],
206 [4979, "1.3.13"],
207 [5108, "1.4.0"]]
208 for h in history:
209 if rev <= h[0]:
210 return h[1]
211 return "SVN"
212
213
214 def get_changelog():
215 """
216 read a complete svn2cl Changelog file and merge commits on one line
217 """
218 lines = open("readers/ChangeLog").readlines()
219 changelog = list()
220 p = list()
221
222 for line in lines:
223 # the lines starts with a year (2000-2099)
224 if line.startswith('20'):
225 changelog.append("".join(p).replace('\n', ''))
226 p = [line]
227 else:
228 p.append(line)
229
230 # add the last line
231 changelog.append("".join(p).replace('\n', ''))
232
233 return changelog
234
235
236 def get_driver_revision(reader, changelog):
237 """
238 search a log line containing the reader string
239 """
240 found = None
241 for line in changelog:
242 if reader in line:
243 found = line
244
245 if found:
246 result = re.search('\\* \\[r(\d*)\\]', found)
247 if result:
248 # revision is the inner matching pattern
249 return int(result.group(1))
250 else:
251 print "reader %s not found in ChangeLog" % reader
252 # fake SVN revision number high enough to be considered as
253 # unreleased
254 return 999999999
255
256
257 def get_by_manufacturer(readers):
258 """
259 return a dict of the readers grouped by manufacturer
260 d['manufacturer'] is a list of the manufacturer's readers
261 """
262 d = {}
263 for r in readers.keys():
264 d.setdefault(readers[r]['iManufacturer'], []).append(r)
265 return d
266
267
268 def generate_page(section, title, comment, readers):
269 """
270 generate a web page for the corresponding section
271 """
272 # sort the readers by manufacturers
273 manufacturer_readers = get_by_manufacturer(readers)
274 manufacturers = list(manufacturer_readers)
275 # sort the manufacturers list alphabetically
276 manufacturers.sort(key=str.lower)
277
278 template = templayer.HTMLTemplate("webpage.template")
279 file_writer = template.start_file(file=file(section + ".html", "w"))
280 main_layer = file_writer.open(date=time.asctime(),
281 title=title, comment=comment, section=section)
282
283 # for each manufacturer
284 for m in manufacturers:
285 main_layer.write_layer('manufacturer', manufacturer=m)
286
287 # for each reader
288 for r in sorted(manufacturer_readers[m]):
289 note_layer = main_layer.open_layer('reader',
290 manufacturer=m,
291 product=readers[r]['iProduct'],
292 idVendor=readers[r]['idVendor'],
293 idProduct=readers[r]['idProduct'],
294 image="img/" + readers[r].get('image', "no_image.png"))
295
296 note_layer.write_layer('descriptor', descriptor="readers/%s" % r)
297
298 url = readers[r].get('url', "")
299 if url:
300 note_layer.write_layer('url',
301 url=url,
302 manufacturer=m,
303 product=readers[r]['iProduct'])
304
305 features = ", ".join(readers[r].get('features', ""))
306 if features:
307 note_layer.write_layer('features', features=features)
308
309 note = readers[r].get('note', "").split('\n')
310 for n in note:
311 note_layer.write_layer('note', contents=n)
312
313 note_layer.write_layer('release',
314 release=readers[r]['release'])
315
316 file_writer.close()
317
318
319 def generate_table(readers, field, index, fields):
320 """
321 generate a web page with all the reader attributes
322 readers are in the order given by index
323 """
324
325 header = """<?xml version="1.0" encoding="UTF-8"?>
326 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
327 <html>
328 <head>
329 <title>%s</title>
330 <link rel="stylesheet" type="text/css" href="default.css">
331 <link rel="stylesheet" type="text/css" href="matrix.css">
332 </head>
333 <body>"""
334
335 footer = """
336 <script type="text/javascript">
337 var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
338 document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
339 </script>
340 <script type="text/javascript">
341 try {
342 var pageTracker = _gat._getTracker("UA-2404298-2");
343 pageTracker._trackPageview();
344 } catch(err) {}</script>
345 <p>Ludovic Rousseau</p>
346 </body>
347 </html>"""
348
349 documentation = """
350 <p>Click on the column header to sort by that column.</p>
351 <p>The background color indicates the section of the reader:</p>
352 <table border="1" summary="color codes">
353 <tr>
354 <td class="supported">supported</td>
355 <td class="shouldwork">should work</td>
356 <td class="unsupported">unsupported</td>
357 </tr></table>
358 """
359
360 file = open(field + ".html", "w")
361 title = "Readers sorted by '%s' field" % field
362 file.write(header % title)
363
364 file.write("<h1>" + title + "</h1>")
365 file.write(documentation)
366
367 file.write('<table border="1" summary="">\n')
368
369 file.write('<tr>')
370 file.write("<th>#</th>")
371 for f in fields:
372 file.write("<th><a href='%s'>%s</a></th>" % (f + ".html", f))
373 file.write('</tr>\n')
374
375 num = 0
376 for r in index:
377 num += 1
378 file.write('<tr class="%s">' % readers[r]['section'])
379 file.write("<td>%d</td>" % num)
380 for f in fields:
381 if f == 'iProduct':
382 file.write("<td><a href='%s.html#%s%s'>%s</a></td>" % (readers[r]['section'], readers[r]['idVendor'], readers[r]['idProduct'], readers[r][f]))
383 elif f == 'iManufacturer':
384 file.write('<td><a onmouseover="">%s<img src="%s" alt="image"></a></td>' % (readers[r][f], "img/" + readers[r].get('image', "no_image.png")))
385 elif f == 'image':
386 file.write('<td><img src="%s" height="100" alt="image"></td>' % ("img/" + readers[r].get('image', "no_image.png")))
387 else:
388 file.write("<td>%s</td>" % readers[r].get(f, ""))
389 file.write('</tr>\n')
390
391 file.write('</table>\n')
392
393 file.write("<hr><p>Generated: %s</p>" % time.asctime())
394
395 file.write(footer)
396 file.close()
397
398
399 def generate_tables(readers):
400 """
401 generate all the web page tables with all the fields values
402 """
403 fields = ['section', 'iManufacturer', 'iProduct', 'image', 'idVendor',
404 'idProduct', 'bNumEndpoints', 'bInterfaceClass', 'bcdCCID',
405 'bMaxSlotIndex', 'bVoltageSupport', 'dwProtocols',
406 'dwDefaultClock', 'dwMaximumClock', 'dwDataRate',
407 'dwMaxDataRate', 'dwMaxIFSD', 'dwSynchProtocols',
408 'dwMechanical', 'dwFeatures', 'dwMaxCCIDMessageLength',
409 'bClassGetResponse', 'bClassEnveloppe', 'wLcdLayout',
410 'bPINSupport', 'bMaxCCIDBusySlots', 'features', 'note',
411 'release']
412
413 for f in fields:
414 index = list()
415 for r in readers.keys():
416 index.append([readers[r].get(f, ""), r])
417 if f == 'section':
418 # hack to sort in the order supported, shouldwork, unsupported
419 index = [(s.replace('supported', 'asupported'), r) for s, r in index]
420 if f in ['dwDefaultClock', 'dwMaximumClock', 'dwDataRate',
421 'dwMaxDataRate', 'dwMaxIFSD', 'dwMaxCCIDMessageLength']:
422 # convert from text to decimal
423 index = [(float(s.split(' ')[0]) * 1000, r) for s, r in index]
424 if f == 'release':
425 index = [(release2int(s), r) for s, r in index]
426 index.sort()
427 sorted_fields = list(fields)
428 sorted_fields.remove(f)
429 sorted_fields.insert(0, f)
430 generate_table(readers, f, [r for f, r in index], sorted_fields)
431
432
433 def release2int(r):
434 rr = r.split('.')
435 if len(rr) > 1:
436 return int(rr[2]) + int(rr[1]) * 1000 + int(rr[0]) * 1000 * 1000
437 else:
438 # SVN version
439 return 99*1000*1000
440
441 if __name__ == "__main__":
442 path = "../trunk/Drivers/ccid/readers/"
443
444 supported_readers = parse_ini(path, "supported")
445 shouldwork_readers = parse_ini(path, "shouldwork")
446 unsupported_readers = parse_ini(path, "unsupported")
447
448 # all_readers contain the union of the 3 lists
449 all_readers = dict(supported_readers)
450 all_readers.update(shouldwork_readers)
451 all_readers.update(unsupported_readers)
452
453 check_list(path, all_readers.keys())
454 check_supported(path, all_readers)
455
456 get_driver_version(supported_readers)
457 get_driver_version(shouldwork_readers)
458 get_driver_version(unsupported_readers)
459
460 generate_page("supported", "Supported CCID readers/ICCD tokens", "If you are a reader manufacturer and your reader is not listed here then contact me at ludovic.rousseau@free.fr", supported_readers)
461 generate_page("shouldwork", "Should work but untested by me", "The CCID readers and ICCD tokens listed bellow should work with the driver but have not be validated by me. I would like to get these readers to perform test and validation and move them in the supported list above. If you are one of the manufacturers, please, contact me at ludovic.rousseau@free.fr.", shouldwork_readers)
462 generate_page("unsupported", "Unsupported or partly supported CCID readers", "These readers have problems or serious limitations.", unsupported_readers)
463
464 generate_tables(all_readers)

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.5