Beispiel #1
0
 def collect_data(self, ids, tdir):
     from calibre.ebooks.metadata.opf2 import metadata_to_opf
     data = {}
     for i in set(ids):
         mi = self.db.get_metadata(i, index_is_id=True, get_cover=True,
                 cover_as_data=True)
         opf = metadata_to_opf(mi)
         if isbytestring(opf):
             opf = opf.decode('utf-8')
         cpath = None
         if mi.cover_data and mi.cover_data[1]:
             cpath = os.path.join(tdir, 'cover_%s.jpg'%i)
             with lopen(cpath, 'wb') as f:
                 f.write(mi.cover_data[1])
             if isbytestring(cpath):
                 cpath = cpath.decode(filesystem_encoding)
         formats = {}
         if mi.formats:
             for fmt in mi.formats:
                 fpath = os.path.join(tdir, 'fmt_%s.%s'%(i, fmt.lower()))
                 with lopen(fpath, 'wb') as f:
                     try:
                         self.db.copy_format_to(i, fmt, f, index_is_id=True)
                     except NoSuchFormat:
                         continue
                     else:
                         if isbytestring(fpath):
                             fpath = fpath.decode(filesystem_encoding)
                         formats[fmt.lower()] = fpath
         data[i] = [opf, cpath, formats, mi.last_modified.isoformat()]
     return data
Beispiel #2
0
def pynocase(one, two, encoding='utf-8'):
    if isbytestring(one):
        try:
            one = one.decode(encoding, 'replace')
        except:
            pass
    if isbytestring(two):
        try:
            two = two.decode(encoding, 'replace')
        except:
            pass
    return cmp(one.lower(), two.lower())
Beispiel #3
0
    def __init__(self, path):
        self.handle_map = {}

        import win32file, winerror
        from pywintypes import error
        from collections import defaultdict

        if isbytestring(path):
            path = path.decode(filesystem_encoding)

        if not os.path.exists(path):
            return

        names = os.listdir(path)
        name_to_fileid = {x:windows_get_fileid(os.path.join(path, x)) for x in names}
        fileid_to_names = defaultdict(set)
        for name, fileid in name_to_fileid.iteritems():
            fileid_to_names[fileid].add(name)

        for x in names:
            f = os.path.normcase(os.path.abspath(os.path.join(path, x)))
            if not os.path.isfile(f):
                continue
            try:
                # Ensure the file is not read-only
                win32file.SetFileAttributes(f, win32file.FILE_ATTRIBUTE_NORMAL)
            except:
                pass

            try:
                h = win32file.CreateFile(f, win32file.GENERIC_READ,
                        win32file.FILE_SHARE_DELETE, None,
                        win32file.OPEN_EXISTING, win32file.FILE_FLAG_SEQUENTIAL_SCAN, 0)
            except error as e:
                if getattr(e, 'winerror', 0) == winerror.ERROR_SHARING_VIOLATION:
                    # The file could be a hardlink to an already opened file,
                    # in which case we use the same handle for both files
                    fileid = name_to_fileid[x]
                    found = False
                    if fileid is not None:
                        for other in fileid_to_names[fileid]:
                            other = os.path.normcase(os.path.abspath(os.path.join(path, other)))
                            if other in self.handle_map:
                                self.handle_map[f] = self.handle_map[other]
                                found = True
                                break
                    if found:
                        continue

                self.close_handles()
                if getattr(e, 'winerror', 0) == winerror.ERROR_SHARING_VIOLATION:
                    err = IOError(errno.EACCES,
                            _('File is open in another process'))
                    err.filename = f
                    raise err
                raise
            except:
                self.close_handles()
                raise
            self.handle_map[f] = h
Beispiel #4
0
 def edit_file(self, name, syntax, use_template=None):
     editor = editors.get(name, None)
     if editor is None:
         if use_template is None:
             data = current_container().raw_data(name)
             if isbytestring(data) and syntax in {
                     'html', 'css', 'text', 'xml'
             }:
                 try:
                     data = data.decode('utf-8')
                 except UnicodeDecodeError:
                     return error_dialog(
                         self.gui,
                         _('Cannot decode'),
                         _('Cannot edit %s as it appears to be in an unknown character encoding'
                           ) % name,
                         show=True)
         else:
             data = use_template
         editor = editors[name] = editor_from_syntax(
             syntax, self.gui.editor_tabs)
         self.init_editor(name,
                          editor,
                          data,
                          use_template=bool(use_template))
     self.show_editor(name)
     return editor
Beispiel #5
0
    def __init__(self, path):
        self.handle_map = {}

        import win32file, winerror
        from pywintypes import error
        from collections import defaultdict

        if isbytestring(path):
            path = path.decode(filesystem_encoding)

        if not os.path.exists(path):
            return

        names = os.listdir(path)
        name_to_fileid = {x:windows_get_fileid(os.path.join(path, x)) for x in names}
        fileid_to_names = defaultdict(set)
        for name, fileid in name_to_fileid.iteritems():
            fileid_to_names[fileid].add(name)

        for x in names:
            f = os.path.normcase(os.path.abspath(os.path.join(path, x)))
            if not os.path.isfile(f):
                continue
            try:
                # Ensure the file is not read-only
                win32file.SetFileAttributes(f, win32file.FILE_ATTRIBUTE_NORMAL)
            except:
                pass

            try:
                h = win32file.CreateFile(f, win32file.GENERIC_READ,
                        win32file.FILE_SHARE_DELETE, None,
                        win32file.OPEN_EXISTING, win32file.FILE_FLAG_SEQUENTIAL_SCAN, 0)
            except error as e:
                if getattr(e, 'winerror', 0) == winerror.ERROR_SHARING_VIOLATION:
                    # The file could be a hardlink to an already opened file,
                    # in which case we use the same handle for both files
                    fileid = name_to_fileid[x]
                    found = False
                    if fileid is not None:
                        for other in fileid_to_names[fileid]:
                            other = os.path.normcase(os.path.abspath(os.path.join(path, other)))
                            if other in self.handle_map:
                                self.handle_map[f] = self.handle_map[other]
                                found = True
                                break
                    if found:
                        continue

                self.close_handles()
                if getattr(e, 'winerror', 0) == winerror.ERROR_SHARING_VIOLATION:
                    err = IOError(errno.EACCES,
                            _('File is open in another process'))
                    err.filename = f
                    raise err
                raise
            except:
                self.close_handles()
                raise
            self.handle_map[f] = h
Beispiel #6
0
    def ajax_search(self, query='', sort='title', offset=0, num=25,
            sort_order='asc'):
        '''
        Return the books (as list of ids) matching the specified search query.
        '''

        try:
            num = int(num)
        except:
            raise cherrypy.HTTPError(404, "Invalid num: %r"%num)
        try:
            offset = int(offset)
        except:
            raise cherrypy.HTTPError(404, "Invalid offset: %r"%offset)
        sfield = self.db.data.sanitize_sort_field_name(sort)
        if sfield not in self.db.field_metadata.sortable_field_keys():
            raise cherrypy.HTTPError(404, '%s is not a valid sort field'%sort)

        if isbytestring(query):
            query = query.decode('UTF-8')
        ids = list(self.search_for_books(query))
        self.db.data.multisort(fields=[(sfield, sort_order == 'asc')], subsort=True,
                only_ids=ids)
        total_num = len(ids)
        ids = ids[offset:offset+num]
        return {
                'total_num': total_num, 'sort_order':sort_order,
                'offset':offset, 'num':len(ids), 'sort':sort,
                'base_url':absurl(self.opts.url_prefix, '/ajax/search'),
                'query': query,
                'book_ids':ids
        }
Beispiel #7
0
def create_mail2(from_, to, subject, text=None, attachment_data=None, attachment_type=None, attachment_name=None):
    assert text or attachment_data

    from email.mime.multipart import MIMEMultipart
    from email.utils import formatdate
    from email import encoders

    mail = MIMEMultipart()
    mail['Subject'] = subject
    mail['To'] = to
    mail['From'] = from_
    mail['Date'] = formatdate(localtime=True)
    mail.preamble = 'You will not see this in a MIME-aware mail reader.\n'

    if text is not None:
        if isbytestring(text):
            msg = MIMEText(text)
        else:
            msg = MIMEText(text, 'plain', 'utf-8')
        mail.attach(msg)

    if attachment_data is not None:
        assert attachment_data and attachment_name
        name = Header(attachment_name, 'utf-8').encode()
        subtype = 'octet-stream;\n\tcharset="utf-8";\n\tname=%s' % name
        msg = MIMEBase('application', subtype)
        msg.set_payload(attachment_data)
        encoders.encode_base64(msg)
        msg.add_header('Content-Disposition', 'attachment',
                       filename=Header(attachment_name, 'utf-8').encode())
        mail.attach(msg)

    return mail.as_string()
def split_txt(txt, epub_split_size_kb=0):
    '''
    Ensure there are split points for converting
    to EPUB. A mis-detected paragraph type can
    result in the entire document being one giant
    paragraph. In this case the EPUB parser will not
    be able to determine where to split the file
    to accommodate the EPUB file size limitation
    and will fail.
    '''
    # Takes care if there is no point to split
    if epub_split_size_kb > 0:
        if isinstance(txt, unicode_type):
            txt = txt.encode('utf-8')
        if len(txt) > epub_split_size_kb * 1024:
            chunk_size = max(16, epub_split_size_kb - 32) * 1024
            # if there are chunks with a superior size then go and break
            parts = txt.split(b'\n\n')
            if parts and max(map(len, parts)) > chunk_size:
                txt = b'\n\n'.join(
                    split_string_separator(line, chunk_size) for line in parts
                )
    if isbytestring(txt):
        txt = txt.decode('utf-8')

    return txt
Beispiel #9
0
    def __init__(self, library_path, db):
        if isbytestring(library_path):
            library_path = library_path.decode(filesystem_encoding)
        self.src_library_path = os.path.abspath(library_path)
        self.db = db

        self.is_case_sensitive = db.is_case_sensitive

        self.all_authors = frozenset([x[1] for x in db.all_authors()])
        self.all_ids = frozenset([id_ for id_ in db.all_ids()])
        self.all_dbpaths = frozenset(self.dbpath(id_) for id_ in self.all_ids)
        self.all_lc_dbpaths = frozenset([f.lower() for f in self.all_dbpaths])

        self.db_id_regexp = re.compile(r'^.* \((\d+)\)$')

        self.dirs = []
        self.book_dirs = []

        self.potential_authors = {}
        self.invalid_authors = []
        self.extra_authors = []

        self.invalid_titles = []
        self.extra_titles = []

        self.unknown_book_files = []
        self.missing_formats = []
        self.extra_formats = []
        self.extra_files = []

        self.missing_covers = []
        self.extra_covers = []

        self.failed_folders = []
Beispiel #10
0
    def __init__(self, library_path, db):
        if isbytestring(library_path):
            library_path = library_path.decode(filesystem_encoding)
        self.src_library_path = os.path.abspath(library_path)
        self.db = db

        self.is_case_sensitive = db.is_case_sensitive

        self.all_authors = frozenset([x[1] for x in db.all_authors()])
        self.all_ids = frozenset([id_ for id_ in db.all_ids()])
        self.all_dbpaths = frozenset(self.dbpath(id_) for id_ in self.all_ids)
        self.all_lc_dbpaths = frozenset([f.lower() for f in self.all_dbpaths])

        self.db_id_regexp = re.compile(r'^.* \((\d+)\)$')

        self.dirs = []
        self.book_dirs = []

        self.potential_authors = {}
        self.invalid_authors = []
        self.extra_authors = []

        self.invalid_titles = []
        self.extra_titles = []

        self.unknown_book_files = []
        self.missing_formats = []
        self.extra_formats = []
        self.extra_files = []

        self.missing_covers = []
        self.extra_covers = []

        self.failed_folders = []
Beispiel #11
0
def get_library_path(gui):
    # gets the current library path from a gui
    db = gui.library_view.model().db
    lp = db.library_path
    if isbytestring(lp):
        lp = lp.decode(filesystem_encoding)
    return lp
Beispiel #12
0
 def library_name(self):
     db = self.gui.library_view.model().db
     path = db.library_path
     if isbytestring(path):
         path = path.decode(filesystem_encoding)
     path = path.replace(os.sep, '/')
     return self.stats.pretty(path)
Beispiel #13
0
    def _generate(self, article, style=None, extra_css=None):
        content = article.content if article.content else ''
        summary = article.summary if article.summary else ''
        text = content if len(content) > len(summary) else summary
        head = HEAD(TITLE(article.title))
        if style:
            head.append(STYLE(style, type='text/css'))
        if extra_css:
            head.append(STYLE(extra_css, type='text/css'))

        if isbytestring(text):
            text = text.decode('utf-8', 'replace')
        elements = html.fragments_fromstring(text)
        self.root = HTML(head,
                BODY(H2(article.title), DIV()))
        div = self.root.find('body').find('div')
        if elements and isinstance(elements[0], unicode_type):
            div.text = elements[0]
            elements = list(elements)[1:]
        for elem in elements:
            if hasattr(elem, 'getparent'):
                elem.getparent().remove(elem)
            else:
                elem = SPAN(elem)
            div.append(elem)
Beispiel #14
0
    def _generate(self, article, style=None, extra_css=None):
        content = article.content if article.content else ''
        summary = article.summary if article.summary else ''
        text = content if len(content) > len(summary) else summary
        head = HEAD(TITLE(article.title))
        if style:
            head.append(STYLE(style, type='text/css'))
        if extra_css:
            head.append(STYLE(extra_css, type='text/css'))

        if isbytestring(text):
            text = text.decode('utf-8', 'replace')
        elements = html.fragments_fromstring(text)
        self.root = HTML(head,
                BODY(H2(article.title), DIV()))
        div = self.root.find('body').find('div')
        if elements and isinstance(elements[0], unicode_type):
            div.text = elements[0]
            elements = list(elements)[1:]
        for elem in elements:
            if hasattr(elem, 'getparent'):
                elem.getparent().remove(elem)
            else:
                elem = SPAN(elem)
            div.append(elem)
Beispiel #15
0
def object_to_unicode(obj, enc=preferred_encoding):
    def dec(x):
        return x.decode(enc, 'replace')

    if isbytestring(obj):
        return dec(obj)
    if isinstance(obj, (list, tuple)):
        return [dec(x) if isbytestring(x) else x for x in obj]
    if isinstance(obj, dict):
        ans = {}
        for k, v in obj.items():
            k = object_to_unicode(k)
            v = object_to_unicode(v)
            ans[k] = v
        return ans
    return obj
Beispiel #16
0
def clean_txt(txt):
    '''
    Run transformations on the text to put it into
    consistent state.
    '''
    if isbytestring(txt):
        txt = txt.decode('utf-8', 'replace')
    # Strip whitespace from the end of the line. Also replace
    # all line breaks with \n.
    txt = '\n'.join([line.rstrip() for line in txt.splitlines()])

    # Replace whitespace at the beginning of the line with  
    txt = re.sub('(?m)(?<=^)([ ]{2,}|\t+)(?=.)', '&nbsp;' * 4, txt)

    # Condense redundant spaces
    txt = re.sub('[ ]{2,}', ' ', txt)

    # Remove blank space from the beginning and end of the document.
    txt = re.sub('^\s+(?=.)', '', txt)
    txt = re.sub('(?<=.)\s+$', '', txt)
    # Remove excessive line breaks.
    txt = re.sub('\n{5,}', '\n\n\n\n', txt)
    # remove ASCII invalid chars : 0 to 8 and 11-14 to 24
    txt = clean_ascii_chars(txt)

    return txt
Beispiel #17
0
def object_to_unicode(obj, enc=preferred_encoding):
    def dec(x):
        return x.decode(enc, "replace")

    if isbytestring(obj):
        return dec(obj)
    if isinstance(obj, (list, tuple)):
        return [dec(x) if isbytestring(x) else x for x in obj]
    if isinstance(obj, dict):
        ans = {}
        for k, v in obj.items():
            k = object_to_unicode(k)
            v = object_to_unicode(v)
            ans[k] = v
        return ans
    return obj
Beispiel #18
0
    def ajax_search(self, query='', sort='title', offset=0, num=25,
            sort_order='asc'):
        '''
        Return the books (as list of ids) matching the specified search query.
        '''

        try:
            num = int(num)
        except:
            raise cherrypy.HTTPError(404, "Invalid num: %r"%num)
        try:
            offset = int(offset)
        except:
            raise cherrypy.HTTPError(404, "Invalid offset: %r"%offset)
        sfield = self.db.data.sanitize_sort_field_name(sort)
        if sfield not in self.db.field_metadata.sortable_field_keys():
            raise cherrypy.HTTPError(404, '%s is not a valid sort field'%sort)

        if isbytestring(query):
            query = query.decode('UTF-8')
        ids = list(self.search_for_books(query))
        self.db.data.multisort(fields=[(sfield, sort_order == 'asc')], subsort=True,
                only_ids=ids)
        total_num = len(ids)
        ids = ids[offset:offset+num]
        return {
                'total_num': total_num, 'sort_order':sort_order,
                'offset':offset, 'num':len(ids), 'sort':sort,
                'base_url':absurl(self.opts.url_prefix, '/ajax/search'),
                'query': query,
                'book_ids':ids
        }
Beispiel #19
0
def split_txt(txt, epub_split_size_kb=0):
    '''
    Ensure there are split points for converting
    to EPUB. A misdetected paragraph type can
    result in the entire document being one giant
    paragraph. In this case the EPUB parser will not
    be able to determine where to split the file
    to accomidate the EPUB file size limitation
    and will fail.
    '''
    # Takes care if there is no point to split
    if epub_split_size_kb > 0:
        if isinstance(txt, unicode):
            txt = txt.encode('utf-8')
        length_byte = len(txt)
        # Calculating the average chunk value for easy splitting as EPUB (+2 as a safe margin)
        chunk_size = long(length_byte / (int(length_byte / (epub_split_size_kb * 1024)) + 2))
        # if there are chunks with a superior size then go and break
        if (len(filter(lambda x: len(x) > chunk_size, txt.split('\n\n')))) :
            txt = '\n\n'.join([split_string_separator(line, chunk_size)
                for line in txt.split('\n\n')])
    if isbytestring(txt):
        txt = txt.decode('utf-8')

    return txt
Beispiel #20
0
 def library_name(self):
     db = self.gui.library_view.model().db
     path = db.library_path
     if isbytestring(path):
         path = path.decode(filesystem_encoding)
     path = path.replace(os.sep, '/')
     return self.stats.pretty(path)
Beispiel #21
0
def clean_txt(txt):
    '''
    Run transformations on the text to put it into
    consistent state.
    '''
    if isbytestring(txt):
        txt = txt.decode('utf-8', 'replace')
    # Strip whitespace from the end of the line. Also replace
    # all line breaks with \n.
    txt = '\n'.join([line.rstrip() for line in txt.splitlines()])

    # Replace whitespace at the beginning of the line with &nbsp;
    txt = re.sub('(?m)(?<=^)([ ]{2,}|\t+)(?=.)', '&nbsp;' * 4, txt)

    # Condense redundant spaces
    txt = re.sub('[ ]{2,}', ' ', txt)

    # Remove blank space from the beginning and end of the document.
    txt = re.sub('^\s+(?=.)', '', txt)
    txt = re.sub('(?<=.)\s+$', '', txt)
    # Remove excessive line breaks.
    txt = re.sub('\n{5,}', '\n\n\n\n', txt)
    # remove ASCII invalid chars : 0 to 8 and 11-14 to 24
    txt = clean_ascii_chars(txt)

    return txt
Beispiel #22
0
def split_txt(txt, epub_split_size_kb=0):
    '''
    Ensure there are split points for converting
    to EPUB. A misdetected paragraph type can
    result in the entire document being one giant
    paragraph. In this case the EPUB parser will not
    be able to determine where to split the file
    to accomidate the EPUB file size limitation
    and will fail.
    '''
    # Takes care if there is no point to split
    if epub_split_size_kb > 0:
        if isinstance(txt, unicode):
            txt = txt.encode('utf-8')
        length_byte = len(txt)
        # Calculating the average chunk value for easy splitting as EPUB (+2 as a safe margin)
        chunk_size = long(length_byte / (int(length_byte /
                                             (epub_split_size_kb * 1024)) + 2))
        # if there are chunks with a superior size then go and break
        if (len(filter(lambda x: len(x) > chunk_size, txt.split('\n\n')))):
            txt = '\n\n'.join([
                split_string_separator(line, chunk_size)
                for line in txt.split('\n\n')
            ])
    if isbytestring(txt):
        txt = txt.decode('utf-8')

    return txt
Beispiel #23
0
 def add_system_font(self, path):
     if isbytestring(path):
         path = path.decode(filesystem_encoding)
     path = os.path.abspath(path)
     ret = self.w.add_system_font(path)
     if ret > 0:
         atexit.register(self.remove_system_font, path)
     return ret
Beispiel #24
0
 def plugin_tweaks_string(self):
     ans = []
     for key, val in self.plugin_tweaks.iteritems():
         ans.extend(['%s = %r'%(key, val), '', ''])
     ans = '\n'.join(ans)
     if isbytestring(ans):
         ans = ans.decode('utf-8')
     return ans
Beispiel #25
0
 def plugin_tweaks_string(self):
     ans = []
     for key, val in self.plugin_tweaks.iteritems():
         ans.extend(['%s = %r' % (key, val), '', ''])
     ans = '\n'.join(ans)
     if isbytestring(ans):
         ans = ans.decode('utf-8')
     return ans
Beispiel #26
0
def windows_get_fileid(path):
    ''' The fileid uniquely identifies actual file contents (it is the same for
    all hardlinks to a file). Similar to inode number on linux. '''
    from calibre_extensions.winutil import get_file_id
    if isbytestring(path):
        path = path.decode(filesystem_encoding)
    with suppress(OSError):
        return get_file_id(path)
Beispiel #27
0
def windows_get_fileid(path):
    ''' The fileid uniquely identifies actual file contents (it is the same for
    all hardlinks to a file). Similar to inode number on linux. '''
    get_file_id = plugins['winutil'][0].get_file_id
    if isbytestring(path):
        path = path.decode(filesystem_encoding)
    with suppress(OSError):
        return get_file_id(path)
Beispiel #28
0
    def generate(self, *args, **kwargs):
        if "style" not in kwargs:
            kwargs["style"] = ""
        for key in kwargs.keys():
            if isbytestring(kwargs[key]):
                kwargs[key] = kwargs[key].decode("utf-8", "replace")
            if kwargs[key] is None:
                kwargs[key] = u""
        args = list(args)
        for i in range(len(args)):
            if isbytestring(args[i]):
                args[i] = args[i].decode("utf-8", "replace")
            if args[i] is None:
                args[i] = u""

        self._generate(*args, **kwargs)

        return self
Beispiel #29
0
    def generate(self, *args, **kwargs):
        if 'style' not in kwargs:
            kwargs['style'] = ''
        for key in kwargs.keys():
            if isbytestring(kwargs[key]):
                kwargs[key] = kwargs[key].decode('utf-8', 'replace')
            if kwargs[key] is None:
                kwargs[key] = u''
        args = list(args)
        for i in range(len(args)):
            if isbytestring(args[i]):
                args[i] = args[i].decode('utf-8', 'replace')
            if args[i] is None:
                args[i] = u''

        self._generate(*args, **kwargs)

        return self
Beispiel #30
0
 def get_fileid(x):
     if isbytestring(x): x = x.decode(filesystem_encoding)
     try:
         h = win32file.CreateFile(x, 0, 0, None, win32file.OPEN_EXISTING,
                 win32file.FILE_FLAG_BACKUP_SEMANTICS, 0)
         data = win32file.GetFileInformationByHandle(h)
     except (error, EnvironmentError):
         return None
     return (data[4], data[8], data[9])
Beispiel #31
0
    def generate(self, *args, **kwargs):
        if 'style' not in kwargs:
            kwargs['style'] = ''
        for key in kwargs.keys():
            if isbytestring(kwargs[key]):
                kwargs[key] = kwargs[key].decode('utf-8', 'replace')
            if kwargs[key] is None:
                kwargs[key] = u''
        args = list(args)
        for i in range(len(args)):
            if isbytestring(args[i]):
                args[i] = args[i].decode('utf-8', 'replace')
            if args[i] is None:
                args[i] = u''

        self._generate(*args, **kwargs)

        return self
Beispiel #32
0
    def browse_template(self, sort, category=True, initial_search=''):

        if not hasattr(self, '__browse_template__') or \
                self.opts.develop:
            self.__browse_template__ = \
                P('content_server/browse/browse.html', data=True).decode('utf-8')

        ans = self.__browse_template__
        scn = 'calibre_browse_server_sort_'

        if category:
            sort_opts = [('rating', _('Average rating')), ('name', _('Name')),
                         ('popularity', _('Popularity'))]
            scn += 'category'
        else:
            scn += 'list'
            fm = self.db.field_metadata
            sort_opts, added = [], set([])
            displayed_custom_fields = custom_fields_to_display(self.db)
            for x in fm.sortable_field_keys():
                if x in ('ondevice', 'formats', 'sort'):
                    continue
                if fm.is_ignorable_field(
                        x) and x not in displayed_custom_fields:
                    continue
                if x == 'comments' or fm[x]['datatype'] == 'comments':
                    continue
                n = fm[x]['name']
                if n not in added:
                    added.add(n)
                    sort_opts.append((x, n))

        ans = ans.replace('{sort_select_label}', xml(_('Sort by') + ':'))
        ans = ans.replace('{sort_cookie_name}', scn)
        ans = ans.replace('{prefix}', self.opts.url_prefix)
        ans = ans.replace('{library}', _('library'))
        ans = ans.replace('{home}', _('home'))
        ans = ans.replace('{Search}', _('Search'))
        opts = [
            '<option %svalue="%s">%s</option>' % (
                'selected="selected" ' if k == sort else '',
                xml(k),
                xml(nl),
            ) for k, nl in sorted(
                sort_opts, key=lambda x: sort_key(operator.itemgetter(1)(x)))
            if k and nl
        ]
        ans = ans.replace('{sort_select_options}',
                          ('\n' + ' ' * 20).join(opts))
        lp = self.db.library_path
        if isbytestring(lp):
            lp = force_unicode(lp, filesystem_encoding)
        ans = ans.replace('{library_name}', xml(os.path.basename(lp)))
        ans = ans.replace('{library_path}', xml(lp, True))
        ans = ans.replace('{initial_search}',
                          xml(initial_search, attribute=True))
        return ans
Beispiel #33
0
def command_check_library(args, dbpath):
    from calibre.library.check_library import CheckLibrary, CHECKS
    parser = check_library_option_parser()
    opts, args = parser.parse_args(args)
    if len(args) != 0:
        parser.print_help()
        return 1

    if opts.library_path is not None:
        dbpath = opts.library_path

    if isbytestring(dbpath):
        dbpath = dbpath.decode(preferred_encoding)

    if opts.report is None:
        checks = CHECKS
    else:
        checks = []
        for r in opts.report.split(','):
            found = False
            for c in CHECKS:
                if c[0] == r:
                    checks.append(c)
                    found = True
                    break
            if not found:
                print _('Unknown report check'), r
                return 1

    if opts.names is None:
        names = []
    else:
        names = [f.strip() for f in opts.names.split(',') if f.strip()]
    if opts.exts is None:
        exts = []
    else:
        exts = [f.strip() for f in opts.exts.split(',') if f.strip()]

    def print_one(checker, check):
        attr = check[0]
        list = getattr(checker, attr, None)
        if list is None:
            return
        if opts.csv:
            for i in list:
                print check[1] + ',' + i[0] + ',' + i[1]
        else:
            print check[1]
            for i in list:
                print '    %-40.40s - %-40.40s'%(i[0], i[1])

    db = LibraryDatabase2(dbpath)
    checker = CheckLibrary(dbpath, db)
    checker.scan_library(names, exts)
    for check in checks:
        print_one(checker, check)
Beispiel #34
0
def command_check_library(args, dbpath):
    from calibre.library.check_library import CheckLibrary, CHECKS
    parser = check_library_option_parser()
    opts, args = parser.parse_args(args)
    if len(args) != 0:
        parser.print_help()
        return 1

    if opts.library_path is not None:
        dbpath = opts.library_path

    if isbytestring(dbpath):
        dbpath = dbpath.decode(preferred_encoding)

    if opts.report is None:
        checks = CHECKS
    else:
        checks = []
        for r in opts.report.split(','):
            found = False
            for c in CHECKS:
                if c[0] == r:
                    checks.append(c)
                    found = True
                    break
            if not found:
                print _('Unknown report check'), r
                return 1

    if opts.names is None:
        names = []
    else:
        names = [f.strip() for f in opts.names.split(',') if f.strip()]
    if opts.exts is None:
        exts = []
    else:
        exts = [f.strip() for f in opts.exts.split(',') if f.strip()]

    def print_one(checker, check):
        attr = check[0]
        list = getattr(checker, attr, None)
        if list is None:
            return
        if opts.csv:
            for i in list:
                print check[1] + ',' + i[0] + ',' + i[1]
        else:
            print check[1]
            for i in list:
                print '    %-40.40s - %-40.40s'%(i[0], i[1])

    db = LibraryDatabase2(dbpath)
    checker = CheckLibrary(dbpath, db)
    checker.scan_library(names, exts)
    for check in checks:
        print_one(checker, check)
Beispiel #35
0
def windows_nlinks(path):
    import win32file
    dwFlagsAndAttributes = win32file.FILE_FLAG_BACKUP_SEMANTICS if os.path.isdir(path) else 0
    if isbytestring(path):
        path = path.decode(filesystem_encoding)
    handle = win32file.CreateFileW(path, win32file.GENERIC_READ, win32file.FILE_SHARE_READ, None, win32file.OPEN_EXISTING, dwFlagsAndAttributes, None)
    try:
        return win32file.GetFileInformationByHandle(handle)[7]
    finally:
        handle.Close()
Beispiel #36
0
 def get_fileid(x):
     if isbytestring(x): x = x.decode(filesystem_encoding)
     try:
         h = win32file.CreateFile(x, 0, 0, None, win32file.OPEN_EXISTING,
                                  win32file.FILE_FLAG_BACKUP_SEMANTICS, 0)
         handles.append(h)
         data = win32file.GetFileInformationByHandle(h)
     except (error, EnvironmentError):
         return None
     return (data[4], data[8], data[9])
Beispiel #37
0
    def browse_category_group(self, category=None, group=None, sort=None):
        if sort == 'null':
            sort = None
        if sort not in ('rating', 'name', 'popularity'):
            sort = 'name'
        try:
            category = unhexlify(category)
            if isbytestring(category):
                category = category.decode('utf-8')
        except:
            raise cherrypy.HTTPError(404, 'invalid category')

        categories = self.categories_cache()
        if category not in categories:
            raise cherrypy.HTTPError(404, 'category not found')

        category_meta = self.db.field_metadata
        try:
            datatype = category_meta[category]['datatype']
        except KeyError:
            datatype = 'text'

        try:
            group = unhexlify(group)
            if isbytestring(group):
                group = group.decode('utf-8')
        except:
            raise cherrypy.HTTPError(404, 'invalid group')

        items = categories[category]
        entries = []
        getter = lambda x: unicode(getattr(x, 'sort', None) or x.name)
        for x in items:
            val = getter(x)
            if not val:
                val = u'A'
            if val.upper().startswith(group):
                entries.append(x)

        sort = self.browse_sort_categories(entries, sort)
        entries = get_category_items(category, entries,
                datatype, self.opts.url_prefix)
        return json.dumps(entries, ensure_ascii=True)
Beispiel #38
0
    def browse_category_group(self, category=None, group=None, sort=None):
        if sort == 'null':
            sort = None
        if sort not in ('rating', 'name', 'popularity'):
            sort = 'name'
        try:
            category = unhexlify(category)
            if isbytestring(category):
                category = category.decode('utf-8')
        except:
            raise cherrypy.HTTPError(404, 'invalid category')

        categories = self.categories_cache()
        if category not in categories:
            raise cherrypy.HTTPError(404, 'category not found')

        category_meta = self.db.field_metadata
        try:
            datatype = category_meta[category]['datatype']
        except KeyError:
            datatype = 'text'

        try:
            group = unhexlify(group)
            if isbytestring(group):
                group = group.decode('utf-8')
        except:
            raise cherrypy.HTTPError(404, 'invalid group')

        items = categories[category]
        entries = []
        getter = lambda x: unicode(getattr(x, 'sort', None) or x.name)
        for x in items:
            val = getter(x)
            if not val:
                val = u'A'
            if val.upper().startswith(group):
                entries.append(x)

        sort = self.browse_sort_categories(entries, sort)
        entries = get_category_items(category, entries,
                datatype, self.opts.url_prefix)
        return json.dumps(entries, ensure_ascii=True)
Beispiel #39
0
 def has_book(self, mi):
     title = mi.title
     if title:
         if isbytestring(title):
             title = title.decode(preferred_encoding, 'replace')
         q = icu_lower(title)
         for title in self.fields['title'].table.book_col_map.itervalues():
             if q == icu_lower(title):
                 return True
     return False
Beispiel #40
0
def windows_nlinks(path):
    import win32file
    dwFlagsAndAttributes = win32file.FILE_FLAG_BACKUP_SEMANTICS if os.path.isdir(path) else 0
    if isbytestring(path):
        path = path.decode(filesystem_encoding)
    handle = win32file.CreateFileW(path, win32file.GENERIC_READ, win32file.FILE_SHARE_READ, None, win32file.OPEN_EXISTING, dwFlagsAndAttributes, None)
    try:
        return win32file.GetFileInformationByHandle(handle)[7]
    finally:
        handle.Close()
Beispiel #41
0
    def create_book_entry(self, mi, cover=None, add_duplicates=True, force_id=None, apply_import_tags=True, preserve_uuid=False):
        if mi.tags:
            mi.tags = list(mi.tags)
        if apply_import_tags:
            _add_newbook_tag(mi)
        if not add_duplicates and self._has_book(mi):
            return
        series_index = (self._get_next_series_num_for(mi.series) if mi.series_index is None else mi.series_index)
        if not mi.authors:
            mi.authors = (_('Unknown'),)
        aus = mi.author_sort if mi.author_sort else self._author_sort_from_authors(mi.authors)
        mi.title = mi.title or _('Unknown')
        if isbytestring(aus):
            aus = aus.decode(preferred_encoding, 'replace')
        if isbytestring(mi.title):
            mi.title = mi.title.decode(preferred_encoding, 'replace')
        conn = self.backend.conn
        if force_id is None:
            conn.execute('INSERT INTO books(title, series_index, author_sort) VALUES (?, ?, ?)',
                         (mi.title, series_index, aus))
        else:
            conn.execute('INSERT INTO books(id, title, series_index, author_sort) VALUES (?, ?, ?, ?)',
                         (force_id, mi.title, series_index, aus))
        book_id = conn.last_insert_rowid()

        mi.timestamp = utcnow() if mi.timestamp is None else mi.timestamp
        mi.pubdate = UNDEFINED_DATE if mi.pubdate is None else mi.pubdate
        if cover is not None:
            mi.cover, mi.cover_data = None, (None, cover)
        self._set_metadata(book_id, mi, ignore_errors=True)
        if preserve_uuid and mi.uuid:
            self._set_field('uuid', {book_id:mi.uuid})
        # Update the caches for fields from the books table
        self.fields['size'].table.book_col_map[book_id] = 0
        row = next(conn.execute('SELECT sort, series_index, author_sort, uuid, has_cover FROM books WHERE id=?', (book_id,)))
        for field, val in zip(('sort', 'series_index', 'author_sort', 'uuid', 'cover'), row):
            if field == 'cover':
                val = bool(val)
            elif field == 'uuid':
                self.fields[field].table.uuid_to_id_map[val] = book_id
            self.fields[field].table.book_col_map[book_id] = val

        return book_id
Beispiel #42
0
def cleanup_tags(tags):
    tags = [x.strip().replace(',', ';') for x in tags if x.strip()]
    tags = [x.decode(preferred_encoding, 'replace')
                if isbytestring(x) else x for x in tags]
    tags = [u' '.join(x.split()) for x in tags]
    ans, seen = [], set([])
    for tag in tags:
        if tag.lower() not in seen:
            seen.add(tag.lower())
            ans.append(tag)
    return ans
Beispiel #43
0
def cleanup_tags(tags):
    tags = [x.strip().replace(',', ';') for x in tags if x.strip()]
    tags = [x.decode(preferred_encoding, 'replace')
                if isbytestring(x) else x for x in tags]
    tags = [u' '.join(x.split()) for x in tags]
    ans, seen = [], set([])
    for tag in tags:
        if tag.lower() not in seen:
            seen.add(tag.lower())
            ans.append(tag)
    return ans
Beispiel #44
0
 def browse_search(self, query="", list_sort=None):
     if isbytestring(query):
         query = query.decode("UTF-8")
     ids = self.search_for_books(query)
     items = [self.db.data.tablerow_for_id(x) for x in ids]
     sort = self.browse_sort_book_list(items, list_sort)
     ids = [x[0] for x in items]
     html = render_book_list(ids, self.opts.url_prefix, suffix=_("in search") + ": " + xml(query))
     return self.browse_template(sort, category=False, initial_search=query).format(
         title=_("Matching books"), script="search_result();", main=html
     )
Beispiel #45
0
 def browse_search(self, query='', list_sort=None):
     if isbytestring(query):
         query = query.decode('UTF-8')
     ids = self.db.search_getting_ids(query.strip(), self.search_restriction)
     items = [self.db.data._data[x] for x in ids]
     sort = self.browse_sort_book_list(items, list_sort)
     ids = [x[0] for x in items]
     html = render_book_list(ids, self.opts.url_prefix,
             suffix=_('in search')+': '+xml(query))
     return self.browse_template(sort, category=False, initial_search=query).format(
             title=_('Matching books'),
             script='search_result();', main=html)
Beispiel #46
0
 def add_system_font(self, path):
     '''
     WARNING: The file you are adding must have execute permissions or
     windows will fail to add it. (ls -l in cygwin to check)
     '''
     if isbytestring(path):
         path = path.decode(filesystem_encoding)
     path = os.path.abspath(path)
     ret = self.w.add_system_font(path)
     if ret > 0:
         atexit.register(self.remove_system_font, path)
     return ret
Beispiel #47
0
 def add_system_font(self, path):
     '''
     WARNING: The file you are adding must have execute permissions or
     windows will fail to add it. (ls -l in cygwin to check)
     '''
     if isbytestring(path):
         path = path.decode(filesystem_encoding)
     path = os.path.abspath(path)
     ret = self.w.add_system_font(path)
     if ret > 0:
         atexit.register(self.remove_system_font, path)
     return ret
Beispiel #48
0
 def browse_search(self, query='', list_sort=None):
     if isbytestring(query):
         query = query.decode('UTF-8')
     ids = self.search_for_books(query)
     items = [self.db.data.tablerow_for_id(x) for x in ids]
     sort = self.browse_sort_book_list(items, list_sort)
     ids = [x[0] for x in items]
     html = render_book_list(ids, self.opts.url_prefix,
             suffix=_('in search')+': '+xml(query))
     return self.browse_template(sort, category=False, initial_search=query).format(
             title=_('Matching books'),
             main=html)
Beispiel #49
0
def windows_get_size(path):
    ''' On windows file sizes are only accurately stored in the actual file,
    not in the directory entry (which could be out of date). So we open the
    file, and get the actual size. '''
    from calibre_extensions import winutil
    if isbytestring(path):
        path = path.decode(filesystem_encoding)
    with closing(
            winutil.create_file(
                path, 0, winutil.FILE_SHARE_READ | winutil.FILE_SHARE_WRITE
                | winutil.FILE_SHARE_DELETE, winutil.OPEN_EXISTING, 0)) as h:
        return winutil.get_file_size(h)
Beispiel #50
0
def windows_get_fileid(path):
    ''' The fileid uniquely identifies actual file contents (it is the same for
    all hardlinks to a file). Similar to inode number on linux. '''
    try:
        get_file_id = plugins['winutil'][0].get_file_id
    except AttributeError:
        # running from source without updating binary
        return old_windows_get_fileid(path)
    if isbytestring(path):
        path = path.decode(filesystem_encoding)
    with suppress(OSError):
        return get_file_id(path)
Beispiel #51
0
 def browse_search(self, query='', list_sort=None):
     if isbytestring(query):
         query = query.decode('UTF-8')
     ids = self.search_for_books(query)
     items = [self.db.data.tablerow_for_id(x) for x in ids]
     sort = self.browse_sort_book_list(items, list_sort)
     ids = [x[0] for x in items]
     html = render_book_list(ids, self.opts.url_prefix,
             suffix=_('in search')+': '+xml(query))
     return self.browse_template(sort, category=False, initial_search=query).format(
             title=_('Matching books'),
             script='search_result();', main=html)
Beispiel #52
0
    def apsw_connect_to_library(self):

        my_db = self.gui.library_view.model().db

        self.lib_path = my_db.library_path
        self.lib_path = self.lib_path.replace(os.sep, '/')
        if isbytestring(self.lib_path):
            self.lib_path = self.lib_path.decode(filesystem_encoding)

        path = my_db.library_path
        if isbytestring(path):
            path = path.decode(filesystem_encoding)
        path = path.replace(os.sep, '/')
        path = os.path.join(path, 'metadata.db')
        path = path.replace(os.sep, '/')

        if isbytestring(path):
            path = path.decode(filesystem_encoding)

        if path.endswith("/"):
            path = path[0:-1]

        if path.count("metadata.db") == 0:
            path = path + "/metadata.db"

        try:
            my_db = apsw.Connection(path)
            is_valid = True
        except Exception as e:
            if DEBUG: print("path to metadata.db is: ", path)
            if DEBUG: print("error: ", as_unicode(e))
            is_valid = False
            return None, None, is_valid

        my_cursor = my_db.cursor()

        mysql = "PRAGMA main.busy_timeout = 5000;"  #PRAGMA busy_timeout = milliseconds;
        my_cursor.execute(mysql)

        return my_db, my_cursor, is_valid
Beispiel #53
0
    def browse_category_group(self, category=None, group=None, sort=None):
        if sort == "null":
            sort = None
        if sort not in ("rating", "name", "popularity"):
            sort = "name"
        try:
            category = unhexlify(category)
            if isbytestring(category):
                category = category.decode("utf-8")
        except:
            raise cherrypy.HTTPError(404, "invalid category")

        categories = self.categories_cache()
        if category not in categories:
            raise cherrypy.HTTPError(404, "category not found")

        category_meta = self.db.field_metadata
        datatype = category_meta[category]["datatype"]

        try:
            group = unhexlify(group)
            if isbytestring(group):
                group = group.decode("utf-8")
        except:
            raise cherrypy.HTTPError(404, "invalid group")

        items = categories[category]
        entries = []
        getter = lambda x: unicode(getattr(x, "sort", x.name))
        for x in items:
            val = getter(x)
            if not val:
                val = u"A"
            if val.upper().startswith(group):
                entries.append(x)

        sort = self.browse_sort_categories(entries, sort)
        entries = get_category_items(category, entries, datatype, self.opts.url_prefix)
        return json.dumps(entries, ensure_ascii=True)
Beispiel #54
0
    def browse_template(self, sort, category=True, initial_search=''):

        if not hasattr(self, '__browse_template__') or \
                self.opts.develop:
            self.__browse_template__ = \
                P('content_server/browse/browse.html', data=True).decode('utf-8')

        ans = self.__browse_template__
        scn = 'calibre_browse_server_sort_'

        if category:
            sort_opts = [('rating', _('Average rating')), ('name',
                _('Name')), ('popularity', _('Popularity'))]
            scn += 'category'
        else:
            scn += 'list'
            fm = self.db.field_metadata
            sort_opts, added = [], set([])
            displayed_custom_fields = custom_fields_to_display(self.db)
            for x in fm.sortable_field_keys():
                if x in ('ondevice', 'formats', 'sort'):
                    continue
                if fm.is_ignorable_field(x) and x not in displayed_custom_fields:
                    continue
                if x == 'comments' or fm[x]['datatype'] == 'comments':
                    continue
                n = fm[x]['name']
                if n not in added:
                    added.add(n)
                    sort_opts.append((x, n))

        ans = ans.replace('{sort_select_label}', xml(_('Sort by')+':'))
        ans = ans.replace('{sort_cookie_name}', scn)
        ans = ans.replace('{prefix}', self.opts.url_prefix)
        ans = ans.replace('{library}', _('library'))
        ans = ans.replace('{home}', _('home'))
        ans = ans.replace('{Search}', _('Search'))
        ans = ans.replace('{nav}', self.nav)
        opts = ['<option %svalue="%s">%s</option>' % (
            'selected="selected" ' if k==sort else '',
            xml(k), xml(nl), ) for k, nl in
                sorted(sort_opts, key=lambda x: sort_key(operator.itemgetter(1)(x))) if k and nl]
        ans = ans.replace('{sort_select_options}', ('\n'+' '*20).join(opts))
        lp = self.db.library_path
        if isbytestring(lp):
            lp = force_unicode(lp, filesystem_encoding)
        ans = ans.replace('{library_name}', xml(os.path.basename(lp)))
        ans = ans.replace('{library_path}', xml(lp, True))
        ans = ans.replace('{initial_search}', xml(initial_search, attribute=True))
        return ans
Beispiel #55
0
def windows_get_size(path):
    ''' On windows file sizes are only accurately stored in the actual file,
    not in the directory entry (which could be out of date). So we open the
    file, and get the actual size. '''
    import win32file
    if isbytestring(path):
        path = path.decode(filesystem_encoding)
    h = win32file.CreateFileW(
        path, 0, win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE | win32file.FILE_SHARE_DELETE,
        None, win32file.OPEN_EXISTING, 0, None)
    try:
        return win32file.GetFileSize(h)
    finally:
        win32file.CloseHandle(h)
Beispiel #56
0
def create_mail(from_,
                to,
                subject,
                text=None,
                attachment_data=None,
                attachment_type=None,
                attachment_name=None):
    assert text or attachment_data

    from email.mime.multipart import MIMEMultipart
    from email.utils import formatdate
    from email import encoders
    import uuid

    outer = MIMEMultipart()
    outer['Subject'] = subject
    outer['To'] = to
    outer['From'] = from_
    outer['Date'] = formatdate(localtime=True)
    outer['Message-Id'] = "<{}@{}>".format(uuid.uuid4(),
                                           get_msgid_domain(from_))
    outer.preamble = 'You will not see this in a MIME-aware mail reader.\n'

    if text is not None:
        from email.mime.text import MIMEText
        if isbytestring(text):
            msg = MIMEText(text)
        else:
            msg = MIMEText(text, 'plain', 'utf-8')
        outer.attach(msg)

    if attachment_data is not None:
        from email.mime.base import MIMEBase
        from email.header import Header
        assert attachment_data and attachment_name
        try:
            maintype, subtype = attachment_type.split('/', 1)
        except AttributeError:
            maintype, subtype = 'application', 'octet-stream'
        msg = MIMEBase(maintype,
                       subtype,
                       name=Header(attachment_name, 'utf-8').encode())
        msg.set_payload(attachment_data)
        encoders.encode_base64(msg)
        msg.add_header('Content-Disposition',
                       'attachment',
                       filename=Header(attachment_name, 'utf-8').encode())
        outer.attach(msg)

    return outer.as_string()
Beispiel #57
0
def windows_get_size(path):
    ''' On windows file sizes are only accurately stored in the actual file,
    not in the directory entry (which could be out of date). So we open the
    file, and get the actual size. '''
    import win32file
    if isbytestring(path):
        path = path.decode(filesystem_encoding)
    h = win32file.CreateFileW(
        path, 0, win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE | win32file.FILE_SHARE_DELETE,
        None, win32file.OPEN_EXISTING, 0, None)
    try:
        return win32file.GetFileSize(h)
    finally:
        win32file.CloseHandle(h)
Beispiel #58
0
def command_backup_metadata(args, dbpath):
    parser = backup_metadata_option_parser()
    opts, args = parser.parse_args(args)
    if len(args) != 0:
        parser.print_help()
        return 1

    if opts.library_path is not None:
        dbpath = opts.library_path
    if isbytestring(dbpath):
        dbpath = dbpath.decode(preferred_encoding)
    db = LibraryDatabase2(dbpath)
    book_ids = None
    if opts.all:
        book_ids = db.all_ids()
    db.dump_metadata(book_ids=book_ids, callback=BackupProgress())
Beispiel #59
0
    def __init__(self, db, callback, parent):
        QDialog.__init__(self, parent)
        self.setupUi(self)
        self.db = db
        self.new_db = None
        self.callback = callback
        self.location.initialize('choose_library_dialog')

        lp = db.library_path
        if isbytestring(lp):
            lp = lp.decode(filesystem_encoding)
        loc = unicode_type(self.old_location.text()).format(lp)
        self.old_location.setText(loc)
        self.browse_button.clicked.connect(self.choose_loc)
        self.empty_library.toggled.connect(self.empty_library_toggled)
        self.copy_structure.setEnabled(False)
Beispiel #60
0
def windows_get_fileid(path):
    ''' The fileid uniquely identifies actual file contents (it is the same for
    all hardlinks to a file). Similar to inode number on linux. '''
    import win32file
    from pywintypes import error
    if isbytestring(path):
        path = path.decode(filesystem_encoding)
    try:
        h = win32file.CreateFileW(path, 0, 0, None, win32file.OPEN_EXISTING,
                win32file.FILE_FLAG_BACKUP_SEMANTICS, 0)
        try:
            data = win32file.GetFileInformationByHandle(h)
        finally:
            win32file.CloseHandle(h)
    except (error, EnvironmentError):
        return None
    return data[4], data[8], data[9]