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

Contents of /website/matrix.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5264 - (hide annotations) (download) (as text)
Mon Sep 20 11:31:40 2010 UTC (2 years, 8 months ago) by rousseau
File MIME type: text/x-python
File size: 16848 byte(s)
Do not convert ' in ' since ' is valid only for XHTML
1 rousseau 3323 #!/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 rousseau 3327 import glob
21     import os
22 rousseau 3342 import ConfigParser
23 rousseau 3343 import pprint
24 rousseau 3345 import templayer
25     import time
26 rousseau 4960 import re
27 rousseau 3323
28 rousseau 3343 pp = pprint.PrettyPrinter(indent=4)
29    
30 rousseau 3489 html_escape_table = {
31     "&": "&",
32     '"': """,
33     ">": ">",
34     "<": "&lt;",
35     }
36    
37 rousseau 4653
38 rousseau 3489 def html_escape(text):
39     """Produce entities within text."""
40 rousseau 4653 L = []
41 rousseau 3489 for c in text:
42 rousseau 4653 L.append(html_escape_table.get(c, c))
43 rousseau 3489 return "".join(L)
44    
45 rousseau 4653
46 rousseau 3327 def parse_reader(path, reader):
47 rousseau 3323 """
48     parse a reader CCID descriptor and return a dictionnary
49     """
50     reader_dict = {}
51 rousseau 4653 reader_file = open(path + reader)
52 rousseau 3323 for line in reader_file.readlines():
53     line = line[0:-1]
54     l = line.strip(" ").split(':')
55     if (len(l) > 1):
56     reader_dict[l[0]] = l[1].strip(" ")
57     reader_file.close()
58     return reader_dict
59    
60 rousseau 4653
61 rousseau 3327 def parse_all(path, reader_list):
62     """
63     parse each reader from list
64     return a dictionnary
65     """
66 rousseau 3323 readers = {}
67 rousseau 3327 for reader in reader_list:
68     p = parse_reader(path, reader)
69 rousseau 3323 readers[reader] = p
70    
71 rousseau 3326 return readers
72    
73 rousseau 4653
74 rousseau 3419 def parse_ini(path, section):
75 rousseau 3343 """
76 rousseau 3489 parse a foobar.ini file to extract all informations
77 rousseau 3343 """
78     config = ConfigParser.ConfigParser()
79 rousseau 3345 # do not use the default case insensitive transform for key value
80     config.optionxform = str
81 rousseau 3419 config.read(section + ".ini")
82 rousseau 3343 reader_list = config.sections()
83    
84     readers = parse_all(path, reader_list)
85     for r in readers.keys():
86 rousseau 3419 readers[r]['section'] = section
87 rousseau 3489 readers[r]['iManufacturer'] = html_escape(readers[r]['iManufacturer'])
88 rousseau 3343 for o in config.options(r):
89 rousseau 3411 if o == 'features':
90     # for the features we use a list
91 rousseau 4653 readers[r][o] = [config.get(r, o)]
92 rousseau 3411 else:
93 rousseau 5171 readers[r][o] = html_escape(config.get(r, o))
94 rousseau 3343
95 rousseau 3410 bPINSupport = int(readers[r]['bPINSupport'], 16)
96 rousseau 3411 if bPINSupport != 0 and not 'features' in readers[r]:
97     readers[r]['features'] = list()
98 rousseau 3410 if bPINSupport & 1:
99 rousseau 3411 readers[r]['features'].append("PIN Verification")
100 rousseau 3410 if bPINSupport & 2:
101 rousseau 3411 readers[r]['features'].append("PIN Modification")
102 rousseau 4315 if int(readers[r]['dwFeatures'], 16) & 0x0800:
103     readers[r].setdefault('features', ["ICCD"])
104 rousseau 3410
105     #pp.pprint(readers["GemPCPinpad.txt"])
106 rousseau 3343 return readers
107    
108 rousseau 4653
109 rousseau 3327 def check_list(path, reader_list):
110 rousseau 3420 """
111     Check that all .txt files are listed
112     """
113 rousseau 3327 cwd = os.getcwd()
114     os.chdir(path)
115     real_list = glob.glob("*.txt")
116     os.chdir(cwd)
117    
118     # check that each reader file is listed
119 rousseau 3328 #print real_list
120 rousseau 3327 for r in reader_list:
121 rousseau 3328 #print "remove ", r
122 rousseau 3327 try:
123     real_list.remove(r)
124     except:
125     print "reader %s not yet listed" % r
126    
127 rousseau 3328 # also remove the non-reader supported_readers.txt file
128     real_list.remove("supported_readers.txt")
129 rousseau 3327
130 rousseau 3328 # some USB descriptor are not listed in readers.txt?
131     if len(real_list) > 0:
132 rousseau 3461 print "Reader(s) not listed in any .ini file:"
133 rousseau 3460 print "\n".join(real_list)
134 rousseau 3461 print ""
135 rousseau 3328
136 rousseau 4653
137 rousseau 3453 def check_supported(path, all_readers):
138     """
139     Check that all .txt files are mentionned in supported_readers.txt
140     """
141     supported_readers = file(path + "supported_readers.txt").readlines()
142     # convert in a long string and in uppercase
143     s = "".join(supported_readers).upper()
144    
145 rousseau 3461 unlisted = list()
146 rousseau 3453 for r in all_readers.keys():
147     pattern = "%s:%s" % (all_readers[r]['idVendor'], all_readers[r]['idProduct'])
148     if not pattern.upper() in s:
149 rousseau 3461 unlisted.append(pattern + " " + r)
150    
151     if len(unlisted) > 0:
152     print "Reader(s) not in supported_readers.txt"
153     print "\n".join(unlisted)
154     print ""
155 rousseau 3453 #pp.pprint(supported_readers)
156    
157 rousseau 4653
158 rousseau 5191 def check_descriptions(path, all_readers):
159     """
160     Check that all readers mentionned in supported_readers.txt have a
161     .txt descriptor
162     """
163     supported_readers = file(path + "supported_readers.txt").readlines()
164    
165     unlisted = list()
166     for line in supported_readers:
167     # skip comments
168     if line.startswith("#"):
169     continue
170    
171     # remove newline
172     line = line.rstrip()
173    
174     # skip empty lines
175     if line is "":
176     continue
177    
178     (vendor, product, name) = line.split(":")
179 rousseau 5192 vendor = int(vendor, 16)
180     product = int(product, 16)
181 rousseau 5191 found = False
182     for r in all_readers.keys():
183     reader = all_readers[r]
184 rousseau 5192 if vendor == int(reader['idVendor'], 16) and product == int(reader['idProduct'], 16):
185 rousseau 5191 found = True
186     if not found:
187     unlisted.append(line)
188    
189     if len(unlisted) > 0:
190     print "Reader(s) without a .txt description"
191     print "\n".join(unlisted)
192     print ""
193     #pp.pprint(supported_readers)
194    
195    
196 rousseau 4960 def get_driver_version(readers):
197     """
198     set the 'release' field for each reader
199     """
200     changelog = get_changelog()
201    
202     for reader in readers.keys():
203     rev = get_driver_revision(reader, changelog)
204     readers[reader]['release'] = driver_revision_to_version(rev)
205    
206    
207     def driver_revision_to_version(rev):
208     """
209     convert a SVN revision in the first release containing this revision
210     """
211     history = [
212     #[ SVN revision, CCID release ]
213 rousseau 5022 [273, "0.1.0"],
214     [342, "0.2.0"],
215     [423, "0.3.0"],
216     [467, "0.3.1"],
217     [552, "0.3.2"],
218     [697, "0.4.0"],
219     [703, "0.4.1"],
220     [1015, "0.9.0"],
221     [1018, "0.9.1"],
222     [1186, "0.9.2"],
223     [1400, "0.9.3"],
224     [1761, "0.9.4"],
225     [1911, "1.0.0"],
226     [2020, "1.0.1"],
227     [2135, "1.1.0"],
228     [2345, "1.2.0"],
229     [2363, "1.2.1"],
230     [2522, "1.3.0"],
231     [2692, "1.3.1"],
232     [2755, "1.3.2"],
233     [2796, "1.3.3"],
234     [2809, "1.3.4"],
235     [2842, "1.3.5"],
236     [2924, "1.3.6"],
237     [2985, "1.3.7"],
238     [3033, "1.3.8"],
239     [3208, "1.3.9"],
240     [3338, "1.3.10"],
241     [4347, "1.3.11"],
242 rousseau 4960 [4931, "1.3.12"],
243 rousseau 5116 [4979, "1.3.13"],
244     [5108, "1.4.0"]]
245 rousseau 4960 for h in history:
246 rousseau 5022 if rev <= h[0]:
247 rousseau 4960 return h[1]
248 rousseau 5022 return "SVN"
249 rousseau 4960
250    
251     def get_changelog():
252     """
253     read a complete svn2cl Changelog file and merge commits on one line
254     """
255 rousseau 5232 lines = open("ccid/readers/ChangeLog").readlines()
256 rousseau 4960 changelog = list()
257     p = list()
258    
259     for line in lines:
260     # the lines starts with a year (2000-2099)
261     if line.startswith('20'):
262     changelog.append("".join(p).replace('\n', ''))
263     p = [line]
264     else:
265     p.append(line)
266    
267 rousseau 5022 # add the last line
268     changelog.append("".join(p).replace('\n', ''))
269    
270 rousseau 4960 return changelog
271    
272    
273     def get_driver_revision(reader, changelog):
274     """
275     search a log line containing the reader string
276     """
277 rousseau 5003 found = None
278 rousseau 4960 for line in changelog:
279 rousseau 5022 if reader in line:
280 rousseau 4960 found = line
281    
282     if found:
283     result = re.search('\\* \\[r(\d*)\\]', found)
284     if result:
285     # revision is the inner matching pattern
286     return int(result.group(1))
287     else:
288 rousseau 5067 print "reader %s not found in ChangeLog" % reader
289     # fake SVN revision number high enough to be considered as
290     # unreleased
291     return 999999999
292 rousseau 4960
293    
294 rousseau 3345 def get_by_manufacturer(readers):
295 rousseau 3420 """
296     return a dict of the readers grouped by manufacturer
297     d['manufacturer'] is a list of the manufacturer's readers
298     """
299 rousseau 3345 d = {}
300     for r in readers.keys():
301     d.setdefault(readers[r]['iManufacturer'], []).append(r)
302     return d
303    
304 rousseau 4653
305 rousseau 3390 def generate_page(section, title, comment, readers):
306 rousseau 3420 """
307     generate a web page for the corresponding section
308     """
309 rousseau 3345 # sort the readers by manufacturers
310 rousseau 3388 manufacturer_readers = get_by_manufacturer(readers)
311 rousseau 3345 manufacturers = list(manufacturer_readers)
312 rousseau 3420 # sort the manufacturers list alphabetically
313 rousseau 3345 manufacturers.sort(key=str.lower)
314    
315 rousseau 3390 template = templayer.HTMLTemplate("webpage.template")
316 rousseau 5232 file_writer = template.start_file(file=file("ccid/" + section + ".html", "w"))
317 rousseau 3390 main_layer = file_writer.open(date=time.asctime(),
318 rousseau 3394 title=title, comment=comment, section=section)
319 rousseau 3419
320 rousseau 3345 # for each manufacturer
321     for m in manufacturers:
322     main_layer.write_layer('manufacturer', manufacturer=m)
323    
324     # for each reader
325     for r in sorted(manufacturer_readers[m]):
326 rousseau 3383 note_layer = main_layer.open_layer('reader',
327 rousseau 4653 manufacturer=m,
328     product=readers[r]['iProduct'],
329     idVendor=readers[r]['idVendor'],
330     idProduct=readers[r]['idProduct'],
331     image="img/" + readers[r].get('image', "no_image.png"))
332 rousseau 3388
333 rousseau 4653 note_layer.write_layer('descriptor', descriptor="readers/%s" % r)
334 rousseau 3391
335 rousseau 3388 url = readers[r].get('url', "")
336     if url:
337 rousseau 3383 note_layer.write_layer('url',
338 rousseau 4653 url=url,
339     manufacturer=m,
340     product=readers[r]['iProduct'])
341 rousseau 3419
342 rousseau 3411 features = ", ".join(readers[r].get('features', ""))
343 rousseau 3384 if features:
344 rousseau 4653 note_layer.write_layer('features', features=features)
345 rousseau 3388
346     note = readers[r].get('note', "").split('\n')
347 rousseau 3383 for n in note:
348 rousseau 4653 note_layer.write_layer('note', contents=n)
349 rousseau 3388
350 rousseau 4960 note_layer.write_layer('release',
351     release=readers[r]['release'])
352    
353 rousseau 3345 file_writer.close()
354 rousseau 3388
355 rousseau 4653
356 rousseau 3422 def generate_table(readers, field, index, fields):
357 rousseau 3420 """
358     generate a web page with all the reader attributes
359     readers are in the order given by index
360     """
361 rousseau 5140
362 rousseau 3419 header = """<?xml version="1.0" encoding="UTF-8"?>
363 rousseau 4245 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
364 rousseau 3419 <html>
365     <head>
366 rousseau 3429 <title>%s</title>
367 rousseau 3419 <link rel="stylesheet" type="text/css" href="default.css">
368 rousseau 4244 <link rel="stylesheet" type="text/css" href="matrix.css">
369 rousseau 5170 <script type="text/javascript">
370     /* <![CDATA[ */
371     (function() {
372     var s = document.createElement('script'), t = document.getElementsByTagName('script')[0];
373    
374     s.type = 'text/javascript';
375     s.async = true;
376     s.src = 'http://api.flattr.com/js/0.5.0/load.js?mode=auto';
377    
378     t.parentNode.insertBefore(s, t);
379     })();
380     /* ]]> */
381     </script>
382 rousseau 3419 </head>
383     <body>"""
384 rousseau 5140
385 rousseau 3419 footer = """
386 rousseau 4215 <script type="text/javascript">
387     var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
388     document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
389     </script>
390     <script type="text/javascript">
391     try {
392     var pageTracker = _gat._getTracker("UA-2404298-2");
393     pageTracker._trackPageview();
394     } catch(err) {}</script>
395 rousseau 3489 <p>Ludovic Rousseau</p>
396 rousseau 3419 </body>
397     </html>"""
398 rousseau 5140
399 rousseau 3429 documentation = """
400 rousseau 5170 <ul>
401     <li>Click on the column header to sort by that column.</li>
402     <li>The background color indicates the section of the reader:
403 rousseau 3489 <table border="1" summary="color codes">
404 rousseau 5140 <tr>
405     <td class="supported">supported</td>
406     <td class="shouldwork">should work</td>
407     <td class="unsupported">unsupported</td>
408 rousseau 5170 </tr></table></li>
409     <li><a class="FlattrButton" style="display:none;"
410     href="http://pcsclite.alioth.debian.org/ccid.html"></a></li>
411     </ul>
412 rousseau 3429 """
413 rousseau 3419
414 rousseau 5232 file = open("ccid/" + field + ".html", "w")
415 rousseau 3429 title = "Readers sorted by '%s' field" % field
416     file.write(header % title)
417 rousseau 3419
418 rousseau 3429 file.write("<h1>" + title + "</h1>")
419     file.write(documentation)
420    
421 rousseau 3419 file.write('<table border="1" summary="">\n')
422    
423     file.write('<tr>')
424 rousseau 3443 file.write("<th>#</th>")
425 rousseau 3419 for f in fields:
426 rousseau 4653 file.write("<th><a href='%s'>%s</a></th>" % (f + ".html", f))
427 rousseau 3419 file.write('</tr>\n')
428    
429 rousseau 3443 num = 0
430 rousseau 3420 for r in index:
431 rousseau 3443 num += 1
432 rousseau 5141 # define color of the line
433     if num % 2:
434     # even line number
435     color = readers[r]['section']
436     else:
437     # odd line number
438     color = readers[r]['section'] + '_odd'
439     file.write('<tr class="%s">' % color)
440    
441 rousseau 3443 file.write("<td>%d</td>" % num)
442 rousseau 3419 for f in fields:
443 rousseau 3422 if f == 'iProduct':
444     file.write("<td><a href='%s.html#%s%s'>%s</a></td>" % (readers[r]['section'], readers[r]['idVendor'], readers[r]['idProduct'], readers[r][f]))
445 rousseau 3438 elif f == 'iManufacturer':
446 rousseau 3489 file.write('<td><a onmouseover="">%s<img src="%s" alt="image"></a></td>' % (readers[r][f], "img/" + readers[r].get('image', "no_image.png")))
447     elif f == 'image':
448     file.write('<td><img src="%s" height="100" alt="image"></td>' % ("img/" + readers[r].get('image', "no_image.png")))
449 rousseau 3422 else:
450 rousseau 3441 file.write("<td>%s</td>" % readers[r].get(f, ""))
451 rousseau 3419 file.write('</tr>\n')
452    
453     file.write('</table>\n')
454    
455 rousseau 3428 file.write("<hr><p>Generated: %s</p>" % time.asctime())
456    
457 rousseau 3419 file.write(footer)
458     file.close()
459    
460 rousseau 4653
461 rousseau 3419 def generate_tables(readers):
462 rousseau 3422 """
463     generate all the web page tables with all the fields values
464     """
465 rousseau 4653 fields = ['section', 'iManufacturer', 'iProduct', 'image', 'idVendor',
466 rousseau 3419 'idProduct', 'bNumEndpoints', 'bInterfaceClass', 'bcdCCID',
467     'bMaxSlotIndex', 'bVoltageSupport', 'dwProtocols',
468     'dwDefaultClock', 'dwMaximumClock', 'dwDataRate',
469     'dwMaxDataRate', 'dwMaxIFSD', 'dwSynchProtocols',
470     'dwMechanical', 'dwFeatures', 'dwMaxCCIDMessageLength',
471     'bClassGetResponse', 'bClassEnveloppe', 'wLcdLayout',
472 rousseau 4960 'bPINSupport', 'bMaxCCIDBusySlots', 'features', 'note',
473     'release']
474 rousseau 3419
475 rousseau 3422 for f in fields:
476     index = list()
477     for r in readers.keys():
478 rousseau 3441 index.append([readers[r].get(f, ""), r])
479 rousseau 3427 if f == 'section':
480     # hack to sort in the order supported, shouldwork, unsupported
481 rousseau 4653 index = [(s.replace('supported', 'asupported'), r) for s, r in index]
482 rousseau 3430 if f in ['dwDefaultClock', 'dwMaximumClock', 'dwDataRate',
483 rousseau 3433 'dwMaxDataRate', 'dwMaxIFSD', 'dwMaxCCIDMessageLength']:
484 rousseau 3430 # convert from text to decimal
485 rousseau 4653 index = [(float(s.split(' ')[0]) * 1000, r) for s, r in index]
486 rousseau 4961 if f == 'release':
487     index = [(release2int(s), r) for s, r in index]
488 rousseau 3422 index.sort()
489 rousseau 3432 sorted_fields = list(fields)
490     sorted_fields.remove(f)
491     sorted_fields.insert(0, f)
492     generate_table(readers, f, [r for f, r in index], sorted_fields)
493 rousseau 3419
494 rousseau 4961
495     def release2int(r):
496     rr = r.split('.')
497 rousseau 5022 if len(rr) > 1:
498     return int(rr[2]) + int(rr[1]) * 1000 + int(rr[0]) * 1000 * 1000
499     else:
500     # SVN version
501     return 99*1000*1000
502 rousseau 4961
503 rousseau 3388 if __name__ == "__main__":
504     path = "../trunk/Drivers/ccid/readers/"
505    
506 rousseau 3419 supported_readers = parse_ini(path, "supported")
507     shouldwork_readers = parse_ini(path, "shouldwork")
508     unsupported_readers = parse_ini(path, "unsupported")
509 rousseau 3388
510 rousseau 3420 # all_readers contain the union of the 3 lists
511     all_readers = dict(supported_readers)
512     all_readers.update(shouldwork_readers)
513     all_readers.update(unsupported_readers)
514    
515     check_list(path, all_readers.keys())
516 rousseau 3453 check_supported(path, all_readers)
517 rousseau 5191 check_descriptions(path, all_readers)
518 rousseau 3420
519 rousseau 4960 get_driver_version(supported_readers)
520     get_driver_version(shouldwork_readers)
521     get_driver_version(unsupported_readers)
522    
523 rousseau 3390 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)
524     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)
525     generate_page("unsupported", "Unsupported or partly supported CCID readers", "These readers have problems or serious limitations.", unsupported_readers)
526 rousseau 3419
527     generate_tables(all_readers)

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.5