diff --git a/resources/recipes/chr_mon.recipe b/resources/recipes/chr_mon.recipe
index f2fec1c24d..79c991efa8 100644
--- a/resources/recipes/chr_mon.recipe
+++ b/resources/recipes/chr_mon.recipe
@@ -1,19 +1,38 @@
+#!/usr/bin/env python
+__license__ = 'GPL v3'
+__author__ = 'Kovid Goyal and Sujata Raman, Lorenzo Vigentini'
+__copyright__ = '2009, Kovid Goyal and Sujata Raman'
+__version__ = 'v1.02'
+__date__ = '10, January 2010'
+__description__ = 'Providing context and clarity on national and international news, peoples and cultures'
+
+'''csmonitor.com'''
+
import re
-from calibre import strftime
from calibre.web.feeds.news import BasicNewsRecipe
class ChristianScienceMonitor(BasicNewsRecipe):
- title = 'Christian Science Monitor'
- description = 'Providing context and clarity on national and international news, peoples and cultures'
- max_articles_per_feed = 20
- __author__ = 'Kovid Goyal and Sujata Raman'
+ author = 'Kovid Goyal, Sujata Raman and Lorenzo Vigentini'
+ description = 'Providing context and clarity on national and international news, peoples and cultures'
+
+ cover_url = 'http://www.csmonitor.com/extension/csm_base/design/csm_design/images/csmlogo_179x46.gif'
+ title = 'Christian Science Monitor'
+ publisher = 'The Christian Science Monitor'
+ category = 'News, politics, culture, economy, general interest'
+
language = 'en'
encoding = 'utf-8'
- no_stylesheets = True
- use_embedded_content = False
+ timefmt = '[%a, %d %b, %Y]'
+ oldest_article = 16
+ max_articles_per_feed = 20
+ use_embedded_content = False
+ recursion = 10
+
+ remove_javascript = True
+ no_stylesheets = True
preprocess_regexps = [ (re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in
@@ -55,33 +74,15 @@ class ChristianScienceMonitor(BasicNewsRecipe):
]
keep_only_tags = [
- dict(name='div', attrs={'id':['story','main']}),
+ dict(name='div', attrs={'id':'mainColumn'}),
]
remove_tags = [
dict(name='div', attrs={'id':['story-tools','videoPlayer','storyRelatedBottom','enlarge-photo','photo-paginate']}),
- dict(name='div', attrs={'class':[ 'spacer3','divvy spacer7','comment','storyIncludeBottom']}),
+ dict(name='div', attrs={'class':['storyToolbar cfx','podStoryRel','spacer3','divvy spacer7','comment','storyIncludeBottom']}),
dict(name='ul', attrs={'class':[ 'centerliststories']}) ,
dict(name='form', attrs={'id':[ 'commentform']}) ,
]
+ remove_tags_after = [ dict(name='div', attrs={'class':[ 'ad csmAd']})]
- def find_articles(self, section):
- ans = []
- for x in section.findAll('head4'):
- title = ' '.join(x.findAll(text=True)).strip()
- a = x.find('a')
- if not a: continue
- href = a['href']
- ans.append({'title':title, 'url':href, 'description':'', 'date': strftime('%a, %d %b')})
-
- #for x in ans:
- # x['url'] += '/output/print'
- return ans
-
- def postprocess_html(self, soup, first_fetch):
- html = soup.find('html')
- if html is None:
- return soup
- html.extract()
- return html
diff --git a/resources/recipes/nytimes_sub.recipe b/resources/recipes/nytimes_sub.recipe
index 2dc15d8f0d..9944f919be 100644
--- a/resources/recipes/nytimes_sub.recipe
+++ b/resources/recipes/nytimes_sub.recipe
@@ -48,7 +48,9 @@ def short_title(self):
return 'NY Times'
def parse_index(self):
+ self.encoding = 'cp1252'
soup = self.index_to_soup('http://www.nytimes.com/pages/todayspaper/index.html')
+ self.encoding = None
def feed_title(div):
return ''.join(div.findAll(text=True, recursive=False)).strip()
diff --git a/resources/recipes/slashdot.recipe b/resources/recipes/slashdot.recipe
index b210079093..dc0067f3ed 100644
--- a/resources/recipes/slashdot.recipe
+++ b/resources/recipes/slashdot.recipe
@@ -9,6 +9,8 @@
class Slashdot(BasicNewsRecipe):
title = u'Slashdot.org'
+ description = '''Tech news. WARNING: This recipe downloads a lot
+ of content and can result in your IP being banned from slashdot.org'''
oldest_article = 7
max_articles_per_feed = 100
language = 'en'
diff --git a/src/calibre/devices/__init__.py b/src/calibre/devices/__init__.py
index 466a9ec20a..feaeffdcd9 100644
--- a/src/calibre/devices/__init__.py
+++ b/src/calibre/devices/__init__.py
@@ -5,7 +5,7 @@
Device drivers.
'''
-import sys, os, time, pprint
+import sys, time, pprint
from functools import partial
from StringIO import StringIO
@@ -29,7 +29,7 @@ def strftime(epoch, zone=time.gmtime):
def debug(ioreg_to_tmp=False, buf=None):
from calibre.customize.ui import device_plugins
- from calibre.devices.scanner import DeviceScanner
+ from calibre.devices.scanner import DeviceScanner, win_pnp_drives
from calibre.constants import iswindows, isosx, __version__
from calibre import prints
oldo, olde = sys.stdout, sys.stderr
@@ -37,19 +37,11 @@ def debug(ioreg_to_tmp=False, buf=None):
if buf is None:
buf = StringIO()
sys.stdout = sys.stderr = buf
- if iswindows:
- import pythoncom
- pythoncom.CoInitialize()
try:
out = partial(prints, file=buf)
out('Version:', __version__)
- wmi = Wmi =None
- if iswindows:
- wmi = __import__('wmi', globals(), locals(), [], -1)
- Wmi = wmi.WMI(find_classes=False)
s = DeviceScanner()
- s.wmi = Wmi
s.scan()
devices = (s.devices)
if not iswindows:
@@ -60,21 +52,9 @@ def debug(ioreg_to_tmp=False, buf=None):
out('USB devices on system:')
out(pprint.pformat(devices))
if iswindows:
- drives = []
+ drives = win_pnp_drives(debug=True)
out('Drives detected:')
- out('\t', '(ID, Partitions, Drive letter)')
- for drive in Wmi.Win32_DiskDrive():
- if drive.Partitions == 0:
- continue
- try:
- partition = drive.associators("Win32_DiskDriveToDiskPartition")[0]
- logical_disk = partition.associators('Win32_LogicalDiskToPartition')[0]
- prefix = logical_disk.DeviceID+os.sep
- drives.append((str(drive.PNPDeviceID), drive.Index, prefix))
- except IndexError:
- drives.append((str(drive.PNPDeviceID), 'No mount points found'))
- for drive in drives:
- out('\t', drive)
+ out(pprint.pformat(drives))
ioreg = None
if isosx:
@@ -84,13 +64,10 @@ def debug(ioreg_to_tmp=False, buf=None):
ioreg = 'Output from mount:\n\n'+mount+'\n\n'+ioreg
connected_devices = []
for dev in device_plugins():
- owmi = getattr(dev, 'wmi', None)
- dev.wmi = Wmi
out('Looking for', dev.__class__.__name__)
connected, det = s.is_device_connected(dev, debug=True)
if connected:
connected_devices.append((dev, det))
- dev.wmi = owmi
errors = {}
success = False
@@ -102,8 +79,6 @@ def debug(ioreg_to_tmp=False, buf=None):
out(' ')
for dev, det in connected_devices:
out('Trying to open', dev.name, '...', end=' ')
- owmi = getattr(dev, 'wmi', None)
- dev.wmi = Wmi
try:
dev.reset(detected_device=det)
dev.open()
@@ -113,8 +88,6 @@ def debug(ioreg_to_tmp=False, buf=None):
errors[dev] = traceback.format_exc()
out('failed')
continue
- finally:
- dev.wmi = owmi
success = True
if hasattr(dev, '_main_prefix'):
out('Main memory:', repr(dev._main_prefix))
@@ -142,7 +115,4 @@ def debug(ioreg_to_tmp=False, buf=None):
finally:
sys.stdout = oldo
sys.stderr = olde
- if iswindows:
- import pythoncom
- pythoncom.CoUninitialize()
diff --git a/src/calibre/devices/interface.py b/src/calibre/devices/interface.py
index 128a0bf45e..3d75f0ae22 100644
--- a/src/calibre/devices/interface.py
+++ b/src/calibre/devices/interface.py
@@ -60,7 +60,7 @@ def print_usb_device_info(self, info):
import traceback
traceback.print_exc()
- def is_usb_connected_windows(self, devices_on_system, pnp_id_iterator, debug=False):
+ def is_usb_connected_windows(self, devices_on_system, debug=False):
def id_iterator():
if hasattr(self.VENDOR_ID, 'keys'):
@@ -85,7 +85,7 @@ def id_iterator():
self.test_bcd_windows(device_id, bcd):
if debug:
self.print_usb_device_info(device_id)
- if self.can_handle_windows(device_id, pnp_id_iterator, debug=debug):
+ if self.can_handle_windows(device_id, debug=debug):
return True
return False
@@ -97,7 +97,7 @@ def test_bcd(self, bcdDevice, bcd):
return True
return False
- def is_usb_connected(self, devices_on_system, pnp_id_iterator, debug=False):
+ def is_usb_connected(self, devices_on_system, debug=False):
'''
Return True, device_info if a device handled by this plugin is currently connected.
@@ -105,7 +105,7 @@ def is_usb_connected(self, devices_on_system, pnp_id_iterator, debug=False):
'''
if iswindows:
return self.is_usb_connected_windows(devices_on_system,
- pnp_id_iterator, debug=debug), None
+ debug=debug), None
vendors_on_system = set([x[0] for x in devices_on_system])
vendors = self.VENDOR_ID if hasattr(self.VENDOR_ID, '__len__') else [self.VENDOR_ID]
@@ -147,7 +147,7 @@ def reset(self, key='-1', log_packets=False, report_progress=None,
"""
raise NotImplementedError()
- def can_handle_windows(self, device_id, pnp_id_iterator, debug=False):
+ def can_handle_windows(self, device_id, debug=False):
'''
Optional method to perform further checks on a device to see if this driver
is capable of handling it. If it is not it should return False. This method
diff --git a/src/calibre/devices/prs500/cli/main.py b/src/calibre/devices/prs500/cli/main.py
index 2b942d4cfc..814c1f06c2 100755
--- a/src/calibre/devices/prs500/cli/main.py
+++ b/src/calibre/devices/prs500/cli/main.py
@@ -9,7 +9,7 @@
import StringIO, sys, time, os
from optparse import OptionParser
-from calibre import __version__, iswindows, __appname__
+from calibre import __version__, __appname__
from calibre.devices.errors import PathError
from calibre.utils.terminfo import TerminalController
from calibre.devices.errors import ArgumentError, DeviceError, DeviceLocked
@@ -198,14 +198,9 @@ def main():
args = args[1:]
dev = None
scanner = DeviceScanner()
- if iswindows:
- import wmi, pythoncom
- pythoncom.CoInitialize()
- scanner.wmi = wmi.WMI(find_classes=False)
scanner.scan()
connected_devices = []
for d in device_plugins():
- d.wmi = scanner.wmi
ok, det = scanner.is_device_connected(d)
if ok:
dev = d
diff --git a/src/calibre/devices/scanner.py b/src/calibre/devices/scanner.py
index 839552e9a7..70d44dc767 100644
--- a/src/calibre/devices/scanner.py
+++ b/src/calibre/devices/scanner.py
@@ -6,6 +6,7 @@
'''
import sys, os
+from threading import RLock
from calibre import iswindows, isosx, plugins, islinux
@@ -22,6 +23,54 @@
except:
raise RuntimeError('Failed to load the usbobserver plugin: %s'%plugins['usbobserver'][1])
+class WinPNPScanner(object):
+
+ def __init__(self):
+ self.scanner = None
+ if iswindows:
+ self.scanner = plugins['winutil'][0].get_removable_drives
+ self.lock = RLock()
+
+ def drive_is_ok(self, letter, debug=False):
+ import win32api, win32file
+ with self.lock:
+ oldError = win32api.SetErrorMode(1) #SEM_FAILCRITICALERRORS = 1
+ try:
+ ans = True
+ try:
+ win32file.GetDiskFreeSpaceEx(letter+':\\')
+ except:
+ ans = False
+ return ans
+ finally:
+ win32api.SetErrorMode(oldError)
+
+ def __call__(self, debug=False):
+ if self.scanner is None:
+ return {}
+ try:
+ drives = self.scanner(debug)
+ except:
+ drives = {}
+ if debug:
+ import traceback
+ traceback.print_exc()
+ remove = set([])
+ for letter in drives:
+ if not self.drive_is_ok(letter, debug=debug):
+ remove.add(letter)
+ for letter in remove:
+ drives.pop(letter)
+ ans = {}
+ for key, val in drives.items():
+ val = [x.upper() for x in val]
+ val = [x for x in val if 'USBSTOR' in x]
+ if val:
+ ans[key+':\\'] = val[-1]
+ return ans
+
+win_pnp_drives = WinPNPScanner()
+
class LinuxScanner(object):
SYSFS_PATH = os.environ.get('SYSFS_PATH', '/sys')
@@ -85,26 +134,13 @@ def __init__(self, *args):
raise RuntimeError('DeviceScanner requires the /sys filesystem to work.')
self.scanner = win_scanner if iswindows else osx_scanner if isosx else linux_scanner
self.devices = []
- self.wmi = None
- self.pnp_ids = set([])
- self.rescan_pnp_ids = True
def scan(self):
'''Fetch list of connected USB devices from operating system'''
self.devices = self.scanner()
- if self.rescan_pnp_ids:
- self.pnp_ids = set([])
-
- def pnp_id_iterator(self):
- if self.wmi is not None and not self.pnp_ids:
- for drive in self.wmi.Win32_DiskDrive():
- if drive.Partitions > 0:
- self.pnp_ids.add(str(drive.PNPDeviceID))
- for x in self.pnp_ids:
- yield x
def is_device_connected(self, device, debug=False):
- return device.is_usb_connected(self.devices, self.pnp_id_iterator, debug=debug)
+ return device.is_usb_connected(self.devices, debug=debug)
def main(args=sys.argv):
diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py
index fd7cf262dc..ccbce861ef 100644
--- a/src/calibre/devices/usbms/device.py
+++ b/src/calibre/devices/usbms/device.py
@@ -203,18 +203,6 @@ def test_vendor():
return False
- def windows_get_drive_prefix(self, drive):
- prefix = None
-
- try:
- partition = drive.associators("Win32_DiskDriveToDiskPartition")[0]
- logical_disk = partition.associators('Win32_LogicalDiskToPartition')[0]
- prefix = logical_disk.DeviceID + os.sep
- except IndexError:
- pass
-
- return prefix
-
def windows_sort_drives(self, drives):
'''
Called to disambiguate main memory and storage card for devices that
@@ -223,8 +211,10 @@ def windows_sort_drives(self, drives):
'''
return drives
- def can_handle_windows(self, device_id, pnp_id_iterator, debug=False):
- for pnp_id in pnp_id_iterator():
+ def can_handle_windows(self, device_id, debug=False):
+ from calibre.devices.scanner import win_pnp_drives
+ drives = win_pnp_drives()
+ for pnp_id in drives.values():
if self.windows_match_device(pnp_id, 'WINDOWS_MAIN_MEM'):
return True
if debug:
@@ -232,29 +222,20 @@ def can_handle_windows(self, device_id, pnp_id_iterator, debug=False):
return False
def open_windows(self):
+ from calibre.devices.scanner import win_pnp_drives
- def matches_q(drive, attr):
- q = getattr(self, attr)
- if q is None: return False
- if isinstance(q, basestring):
- q = [q]
- pnp = str(drive.PNPDeviceID)
- for x in q:
- if x in pnp:
- return True
- return False
-
- time.sleep(8)
+ time.sleep(5)
drives = {}
- c = self.wmi
- for drive in c.Win32_DiskDrive():
- pnp_id = str(drive.PNPDeviceID)
- if self.windows_match_device(pnp_id, 'WINDOWS_CARD_A_MEM') and not drives.get('carda', None):
- drives['carda'] = self.windows_get_drive_prefix(drive)
- elif self.windows_match_device(pnp_id, 'WINDOWS_CARD_B_MEM') and not drives.get('cardb', None):
- drives['cardb'] = self.windows_get_drive_prefix(drive)
- elif self.windows_match_device(pnp_id, 'WINDOWS_MAIN_MEM') and not drives.get('main', None):
- drives['main'] = self.windows_get_drive_prefix(drive)
+ for drive, pnp_id in win_pnp_drives().items():
+ if self.windows_match_device(pnp_id, 'WINDOWS_CARD_A_MEM') and \
+ not drives.get('carda', False):
+ drives['carda'] = drive
+ elif self.windows_match_device(pnp_id, 'WINDOWS_CARD_B_MEM') and \
+ not drives.get('cardb', False):
+ drives['cardb'] = drive
+ elif self.windows_match_device(pnp_id, 'WINDOWS_MAIN_MEM') and \
+ not drives.get('main', False):
+ drives['main'] = drive
if 'main' in drives.keys() and 'carda' in drives.keys() and \
'cardb' in drives.keys():
diff --git a/src/calibre/ebooks/oeb/stylizer.py b/src/calibre/ebooks/oeb/stylizer.py
index d986033418..9f50796615 100644
--- a/src/calibre/ebooks/oeb/stylizer.py
+++ b/src/calibre/ebooks/oeb/stylizer.py
@@ -168,6 +168,7 @@ def __init__(self, tree, path, oeb, profile=PROFILES['PRS505'],
self.rules = rules
self._styles = {}
class_sel_pat = re.compile(r'\.[a-z]+', re.IGNORECASE)
+ capital_sel_pat = re.compile(r'h|[A-Z]+')
for _, _, cssdict, text, _ in rules:
try:
selector = CSSSelector(text)
@@ -176,6 +177,15 @@ def __init__(self, tree, path, oeb, profile=PROFILES['PRS505'],
SelectorSyntaxError):
continue
matches = selector(tree)
+
+ if not matches:
+ ntext = capital_sel_pat.sub(lambda m: m.group().lower(), text)
+ if ntext != text:
+ self.logger.warn('Transformed CSS selector', text, 'to',
+ ntext)
+ selector = CSSSelector(ntext)
+ matches = selector(tree)
+
if not matches and class_sel_pat.match(text):
found = False
for x in tree.xpath('//*[@class]'):
diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py
index 23eb33003c..49a341ae14 100644
--- a/src/calibre/gui2/device.py
+++ b/src/calibre/gui2/device.py
@@ -13,7 +13,6 @@
from calibre.customize.ui import available_input_formats, available_output_formats, \
device_plugins
from calibre.devices.interface import DevicePlugin
-from calibre.constants import iswindows
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
from calibre.utils.ipc.job import BaseJob
from calibre.devices.scanner import DeviceScanner
@@ -85,7 +84,6 @@ def __init__(self, connected_slot, job_manager, sleep_time=2):
self.job_manager = job_manager
self.current_job = None
self.scanner = DeviceScanner()
- self.wmi = None
self.connected_device = None
self.ejected_devices = set([])
@@ -133,7 +131,6 @@ def connected_device_removed(self):
self.connected_device = None
def detect_device(self):
- self.scanner.rescan_pnp_ids = not self.is_device_connected
self.scanner.scan()
if self.is_device_connected:
connected, detected_device = \
@@ -170,30 +167,18 @@ def next(self):
pass
def run(self):
- if iswindows:
- import pythoncom
- pythoncom.CoInitialize()
- wmi = __import__('wmi', globals(), locals(), [], -1)
- self.wmi = wmi.WMI(find_classes=False)
- self.scanner.wmi = self.wmi
- for x in self.devices:
- x.wmi = self.wmi
- try:
- while self.keep_going:
- self.detect_device()
- while True:
- job = self.next()
- if job is not None:
- self.current_job = job
- self.device.set_progress_reporter(job.report_progress)
- self.current_job.run()
- self.current_job = None
- else:
- break
- time.sleep(self.sleep_time)
- finally:
- if iswindows:
- pythoncom.CoUninitialize()
+ while self.keep_going:
+ self.detect_device()
+ while True:
+ job = self.next()
+ if job is not None:
+ self.current_job = job
+ self.device.set_progress_reporter(job.report_progress)
+ self.current_job.run()
+ self.current_job = None
+ else:
+ break
+ time.sleep(self.sleep_time)
def create_job(self, func, done, description, args=[], kwargs={}):
diff --git a/src/calibre/gui2/dialogs/config/config.ui b/src/calibre/gui2/dialogs/config/config.ui
index e638bab1c9..b9306b0f10 100644
--- a/src/calibre/gui2/dialogs/config/config.ui
+++ b/src/calibre/gui2/dialogs/config/config.ui
@@ -7,7 +7,7 @@
0
0
- 838
+ 884
730
@@ -89,7 +89,7 @@
0
0
- 562
+ 608
683
diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py
index cef56e361d..4198fc5684 100644
--- a/src/calibre/gui2/dialogs/metadata_single.py
+++ b/src/calibre/gui2/dialogs/metadata_single.py
@@ -171,7 +171,7 @@ def formats_dropped(self, event, paths):
if self._add_formats(paths):
event.accept()
- def remove_format(self, x=None):
+ def remove_format(self, *args):
rows = self.formats.selectionModel().selectedRows(0)
for row in rows:
self.formats.takeItem(row.row())
diff --git a/src/calibre/gui2/shortcuts.py b/src/calibre/gui2/shortcuts.py
index 5c4ebcc2fe..1281518889 100644
--- a/src/calibre/gui2/shortcuts.py
+++ b/src/calibre/gui2/shortcuts.py
@@ -23,9 +23,10 @@
class Customize(QFrame, Ui_Frame):
- def __init__(self, dup_check, parent=None):
+ def __init__(self, index, dup_check, parent=None):
QFrame.__init__(self, parent)
self.setupUi(self)
+ self.data_model = index.model()
self.setFocusPolicy(Qt.StrongFocus)
self.setAutoFillBackground(True)
self.custom.toggled.connect(self.custom_toggled)
@@ -86,12 +87,21 @@ class Delegate(QStyledItemDelegate):
def __init__(self, parent=None):
QStyledItemDelegate.__init__(self, parent)
self.editing_indices = {}
+ self.closeEditor.connect(self.editing_done)
def to_doc(self, index):
doc = QTextDocument()
doc.setHtml(index.data().toString())
return doc
+ def editing_done(self, editor, hint):
+ remove = None
+ for row, w in self.editing_indices.items():
+ remove = (row, w.data_model.index(row))
+ if remove is not None:
+ self.editing_indices.pop(remove[0])
+ self.sizeHintChanged.emit(remove[1])
+
def sizeHint(self, option, index):
if index.row() in self.editing_indices:
return QSize(200, 200)
@@ -111,7 +121,7 @@ def paint(self, painter, option, index):
painter.restore()
def createEditor(self, parent, option, index):
- w = Customize(index.model().duplicate_check, parent=parent)
+ w = Customize(index, index.model().duplicate_check, parent=parent)
self.editing_indices[index.row()] = w
self.sizeHintChanged.emit(index)
return w
@@ -135,8 +145,6 @@ def setEditorData(self, editor, index):
setattr(editor, 'shortcut%d'%(x+1), seq)
def setModelData(self, editor, model, index):
- self.editing_indices.pop(index.row())
- self.sizeHintChanged.emit(index)
self.closeEditor.emit(editor, self.NoHint)
custom = []
if editor.custom.isChecked():
diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py
index b1267bd772..474345d442 100644
--- a/src/calibre/gui2/widgets.py
+++ b/src/calibre/gui2/widgets.py
@@ -141,8 +141,8 @@ def keyPressEvent(self, event):
if event.key() == Qt.Key_Delete:
self.emit(SIGNAL('delete_format()'))
else:
- QListWidget.keyPressEvent(self, event)
-
+ return QListWidget.keyPressEvent(self, event)
+
class ImageView(QLabel):
diff --git a/src/calibre/utils/windows/winutil.c b/src/calibre/utils/windows/winutil.c
index be504b7fcf..efd8f1400d 100644
--- a/src/calibre/utils/windows/winutil.c
+++ b/src/calibre/utils/windows/winutil.c
@@ -204,23 +204,22 @@ get_registry_property(HDEVINFO hDevInfo, DWORD index, DWORD property, BOOL *iter
return buffer;
}
-static BOOL
-check_device_id(LPTSTR buffer, unsigned int vid, unsigned int pid) {
- WCHAR xVid[9], dVid[9], xPid[9], dPid[9];
- unsigned int j;
- swprintf(xVid, L"vid_%4.4x", vid);
- swprintf(dVid, L"vid_%4.4d", vid);
- swprintf(xPid, L"pid_%4.4x", pid);
- swprintf(dPid, L"pid_%4.4d", pid);
-
- for (j = 0; j < wcslen(buffer); j++) buffer[j] = tolower(buffer[j]);
-
- return ( (wcsstr(buffer, xVid) != NULL || wcsstr(buffer, dVid) != NULL ) &&
- (wcsstr(buffer, xPid) != NULL || wcsstr(buffer, dPid) != NULL )
- );
-}
-
-
+static BOOL
+check_device_id(LPTSTR buffer, unsigned int vid, unsigned int pid) {
+ WCHAR xVid[9], dVid[9], xPid[9], dPid[9];
+ unsigned int j;
+ _snwprintf_s(xVid, 9, _TRUNCATE, L"vid_%4.4x", vid);
+ _snwprintf_s(dVid, 9, _TRUNCATE, L"vid_%4.4d", vid);
+ _snwprintf_s(xPid, 9, _TRUNCATE, L"pid_%4.4x", pid);
+ _snwprintf_s(dPid, 9, _TRUNCATE, L"pid_%4.4d", pid);
+
+ for (j = 0; j < wcslen(buffer); j++) buffer[j] = tolower(buffer[j]);
+
+ return ( (wcsstr(buffer, xVid) != NULL || wcsstr(buffer, dVid) != NULL ) &&
+ (wcsstr(buffer, xPid) != NULL || wcsstr(buffer, dPid) != NULL )
+ );
+}
+
static HDEVINFO
create_device_info_set(LPGUID guid, PCTSTR enumerator, HWND parent, DWORD flags) {
HDEVINFO hDevInfo;
@@ -286,7 +285,7 @@ get_all_removable_disks(struct tagDrives *g_drives)
if(GetVolumeNameForVolumeMountPoint(caDrive, volume, BUFSIZE))
{
g_drives[g_count].letter = caDrive[0];
- wcscpy(g_drives[g_count].volume, volume);
+ wcscpy_s(g_drives[g_count].volume, BUFSIZE, volume);
g_count ++;
}
@@ -515,15 +514,17 @@ winutil_eject_drive(PyObject *self, PyObject *args) {
PSP_DEVICE_INTERFACE_DETAIL_DATA
-get_device_grandparent(HDEVINFO hDevInfo, DWORD index, PWSTR buf, PWSTR volume_id,
- BOOL *iterate) {
+get_device_ancestors(HDEVINFO hDevInfo, DWORD index, PyObject *candidates, BOOL *iterate, BOOL ddebug) {
SP_DEVICE_INTERFACE_DATA interfaceData;
SP_DEVINFO_DATA devInfoData;
BOOL status;
PSP_DEVICE_INTERFACE_DETAIL_DATA interfaceDetailData;
DWORD interfaceDetailDataSize,
reqSize;
- DEVINST parent;
+ DEVINST parent, pos;
+ wchar_t temp[BUFSIZE];
+ int i;
+ PyObject *devid;
interfaceData.cbSize = sizeof (SP_INTERFACE_DEVICE_DATA);
devInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
@@ -549,7 +550,7 @@ get_device_grandparent(HDEVINFO hDevInfo, DWORD index, PWSTR buf, PWSTR volume_i
);
interfaceDetailDataSize = reqSize;
- interfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)PyMem_Malloc(interfaceDetailDataSize+10);
+ interfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)PyMem_Malloc(interfaceDetailDataSize+50);
if ( interfaceDetailData == NULL ) {
PyErr_NoMemory();
return NULL;
@@ -563,38 +564,49 @@ get_device_grandparent(HDEVINFO hDevInfo, DWORD index, PWSTR buf, PWSTR volume_i
interfaceDetailDataSize, // Interface detail data size
&reqSize, // Buffer size required to get the detail data
&devInfoData); // Interface device info
+ if (ddebug) printf("Getting ancestors\n"); fflush(stdout);
if ( status == FALSE ) {PyErr_SetFromWindowsErr(0); PyMem_Free(interfaceDetailData); return NULL;}
- // Get the device instance of parent. This points to USBSTOR.
- CM_Get_Parent(&parent, devInfoData.DevInst, 0);
- // Get the device ID of the USBSTORAGE volume
- CM_Get_Device_ID(parent, volume_id, BUFSIZE, 0);
- // Get the device instance of grand parent. This points to USB root.
- CM_Get_Parent(&parent, parent, 0);
- // Get the device ID of the USB root.
- CM_Get_Device_ID(parent, buf, BUFSIZE, 0);
+ pos = devInfoData.DevInst;
+
+ for(i = 0; i < 10; i++) {
+ // Get the device instance of parent.
+ if (CM_Get_Parent(&parent, pos, 0) != CR_SUCCESS) break;
+ if (CM_Get_Device_ID(parent, temp, BUFSIZE, 0) == CR_SUCCESS) {
+ if (ddebug) wprintf(L"device id: %s\n", temp); fflush(stdout);
+ devid = PyUnicode_FromWideChar(temp, wcslen(temp));
+ if (devid) {
+ PyList_Append(candidates, devid);
+ Py_DECREF(devid);
+ }
+ }
+ pos = parent;
+ }
return interfaceDetailData;
}
static PyObject *
-winutil_get_mounted_volumes_for_usb_device(PyObject *self, PyObject *args) {
- unsigned int vid, pid, length, j;
- HDEVINFO hDevInfo;
- BOOL iterate = TRUE;
+winutil_get_removable_drives(PyObject *self, PyObject *args) {
+ HDEVINFO hDevInfo;
+ BOOL iterate = TRUE, ddebug = FALSE;
PSP_DEVICE_INTERFACE_DETAIL_DATA interfaceDetailData;
DWORD i;
- WCHAR buf[BUFSIZE], volume[BUFSIZE], volume_id[BUFSIZE];
+ unsigned int j, length;
+ WCHAR volume[BUFSIZE];
struct tagDrives g_drives[MAX_DRIVES];
- PyObject *volumes, *key, *val;
+ PyObject *volumes, *key, *candidates, *pdebug = Py_False, *temp;
- if (!PyArg_ParseTuple(args, "ii", &vid, &pid)) {
+ if (!PyArg_ParseTuple(args, "|O", &pdebug)) {
return NULL;
}
+ ddebug = PyObject_IsTrue(pdebug);
+
volumes = PyDict_New();
if (volumes == NULL) return NULL;
+
for (j = 0; j < MAX_DRIVES; j++) g_drives[j].letter = 0;
@@ -609,47 +621,44 @@ winutil_get_mounted_volumes_for_usb_device(PyObject *self, PyObject *args) {
// Enumerate through the set
for (i=0; iterate; i++) {
- interfaceDetailData = get_device_grandparent(hDevInfo, i, buf, volume_id, &iterate);
+ candidates = PyList_New(0);
+ if (candidates == NULL) return PyErr_NoMemory();
+
+ interfaceDetailData = get_device_ancestors(hDevInfo, i, candidates, &iterate, ddebug);
if (interfaceDetailData == NULL) {
PyErr_Print(); continue;
}
- debug("Device num: %d Device Id: %ws\n\n", i, buf);
- if (check_device_id(buf, vid, pid)) {
- debug("Device matches\n\n");
- length = wcslen(interfaceDetailData->DevicePath);
- interfaceDetailData->DevicePath[length] = '\\';
- interfaceDetailData->DevicePath[length+1] = 0;
- if(GetVolumeNameForVolumeMountPoint(interfaceDetailData->DevicePath, volume, BUFSIZE)) {
- for(j = 0; j < MAX_DRIVES; j++) {
- // Compare volume mount point with the one stored earlier.
- // If both match, return the corresponding drive letter.
- if(g_drives[j].letter != 0 && wcscmp(g_drives[j].volume, volume)==0)
- {
- key = PyUnicode_FromWideChar(volume_id, wcslen(volume_id));
- val = PyString_FromFormat("%c", (char)g_drives[j].letter);
- if (key == NULL || val == NULL) {
- PyErr_NoMemory();
- PyMem_Free(interfaceDetailData);
- return NULL;
- }
- PyDict_SetItem(volumes, key, val);
- }
+ length = wcslen(interfaceDetailData->DevicePath);
+ interfaceDetailData->DevicePath[length] = L'\\';
+ interfaceDetailData->DevicePath[length+1] = 0;
+
+ if (ddebug) wprintf(L"Device path: %s\n", interfaceDetailData->DevicePath); fflush(stdout);
+ // On Vista+ DevicePath contains the information we need.
+ temp = PyUnicode_FromWideChar(interfaceDetailData->DevicePath, length);
+ if (temp == NULL) return PyErr_NoMemory();
+ PyList_Append(candidates, temp);
+ Py_DECREF(temp);
+ if(GetVolumeNameForVolumeMountPointW(interfaceDetailData->DevicePath, volume, BUFSIZE)) {
+ if (ddebug) wprintf(L"Volume: %s\n", volume); fflush(stdout);
+
+ for(j = 0; j < MAX_DRIVES; j++) {
+ if(g_drives[j].letter != 0 && wcscmp(g_drives[j].volume, volume)==0) {
+ if (ddebug) printf("Found drive: %c\n", (char)g_drives[j].letter); fflush(stdout);
+ key = PyBytes_FromFormat("%c", (char)g_drives[j].letter);
+ if (key == NULL) return PyErr_NoMemory();
+ PyDict_SetItem(volumes, key, candidates);
+ Py_DECREF(candidates);
+ break;
}
-
- } else {
- debug("Failed to get volume name for volume mount point:\n");
- if (DEBUG) debug("%ws\n\n", format_last_error());
}
- PyMem_Free(interfaceDetailData);
}
-
+ PyMem_Free(interfaceDetailData);
} //for
SetupDiDestroyDeviceInfoList(hDevInfo);
return volumes;
-
}
static PyObject *
@@ -876,21 +885,22 @@ static PyMethodDef WinutilMethods[] = {
"script being run. So to replace sys.argv, you should use "
"sys.argv[1:] = argv()[1:]."},
- {"is_usb_device_connected", winutil_is_usb_device_connected, METH_VARARGS,
- "is_usb_device_connected(vid, pid) -> bool\n\n"
- "Check if the USB device identified by VendorID: vid (integer) and"
- " ProductID: pid (integer) is currently connected."},
+ {"is_usb_device_connected", winutil_is_usb_device_connected, METH_VARARGS,
+ "is_usb_device_connected(vid, pid) -> bool\n\n"
+ "Check if the USB device identified by VendorID: vid (integer) and"
+ " ProductID: pid (integer) is currently connected."},
{"get_usb_devices", winutil_get_usb_devices, METH_VARARGS,
"get_usb_devices() -> list of strings\n\n"
"Return a list of the hardware IDs of all USB devices "
"connected to the system."},
- {"get_mounted_volumes_for_usb_device", winutil_get_mounted_volumes_for_usb_device, METH_VARARGS,
- "get_mounted_volumes_for_usb_device(vid, pid) -> dict\n\n"
- "Return a dictionary of volume_id:drive_letter for all"
- "volumes mounted on the system that belong to the"
- "usb device specified by vid (integer) and pid (integer)."},
+ {"get_removable_drives", winutil_get_removable_drives, METH_VARARGS,
+ "get_removable_drives(debug=False) -> dict\n\n"
+ "Return mapping of all removable drives in the system. Maps drive letters "
+ "to a list of device id strings, atleast one of which will carry the information "
+ "needed for device matching. On Vista+ it is always the last string in the list. "
+ "Note that you should upper case all strings."},
{"set_debug", winutil_set_debug, METH_VARARGS,
"set_debug(bool)\n\nSet debugging mode."