Ejemplo n.º 1
0
def opds_category(ctx, rd, category, which):
    try:
        offset = int(rd.query.get('offset', 0))
    except Exception:
        raise HTTPNotFound('Not found')

    if not which or not category:
        raise HTTPNotFound('Not found')
    rc = RequestContext(ctx, rd)
    page_url = rc.url_for('/opds/category', which=which, category=category)
    up_url = rc.url_for('/opds/navcatalog', which=category)

    which, category = from_hex_unicode(which), from_hex_unicode(category)
    type_ = which[0]
    which = which[1:]
    if type_ == 'I':
        try:
            p = which.rindex(':')
            category = which[p + 1:]
            which = which[:p]
            # This line will toss an exception for composite columns
            which = int(which[:p])
        except Exception:
            # Might be a composite column, where we have the lookup key
            if not (category in rc.db.field_metadata and
                    rc.db.field_metadata[category]['datatype'] == 'composite'):
                raise HTTPNotFound('Tag %r not found' % which)

    categories = rc.get_categories()
    if category not in categories:
        raise HTTPNotFound('Category %r not found' % which)

    if category == 'search':
        try:
            ids = rc.search('search:"%s"' % which)
        except Exception:
            raise HTTPNotFound('Search: %r not understood' % which)
        return get_acquisition_feed(rc, ids, offset, page_url, up_url,
                                    'calibre-search:' + which)

    if type_ != 'I':
        raise HTTPNotFound('Non id categories not supported')

    q = category
    if q == 'news':
        q = 'tags'
    ids = rc.db.get_books_for_category(q, which) & rc.allowed_book_ids()
    sort_by = 'series' if category == 'series' else 'title'

    return get_acquisition_feed(rc,
                                ids,
                                offset,
                                page_url,
                                up_url,
                                'calibre-category:' + category + ':' +
                                unicode_type(which),
                                sort_by=sort_by)
Ejemplo n.º 2
0
def opds_categorygroup(ctx, rd, category, which):
    try:
        offset = int(rd.query.get('offset', 0))
    except Exception:
        raise HTTPNotFound('Not found')

    if not which or not category:
        raise HTTPNotFound('Not found')

    rc = RequestContext(ctx, rd)
    categories = rc.get_categories()
    page_url = rc.url_for('/opds/categorygroup',
                          category=category,
                          which=which)

    category = from_hex_unicode(category)
    if category not in categories:
        raise HTTPNotFound('Category %r not found' % which)
    category_meta = rc.db.field_metadata
    meta = category_meta.get(category, {})
    category_name = meta.get('name', which)
    which = from_hex_unicode(which)
    feed_title = default_feed_title + ' :: ' + (_('By {0} :: {1}').format(
        category_name, which))
    owhich = as_hex_unicode('N' + which)
    up_url = rc.url_for('/opds/navcatalog', which=owhich)
    items = categories[category]

    def belongs(x, which):
        return getattr(x, 'sort', x.name).lower().startswith(which.lower())

    items = [x for x in items if belongs(x, which)]
    if not items:
        raise HTTPNotFound('No items in group %r:%r' % (category, which))
    updated = rc.last_modified()

    id_ = 'calibre-category-group-feed:' + category + ':' + which

    max_items = rc.opts.max_opds_items
    offsets = Offsets(offset, max_items, len(items))
    items = list(items)[offsets.offset:offsets.offset + max_items]

    rc.outheaders['Last-Modified'] = http_date(timestampfromdt(updated))

    return CategoryFeed(items,
                        category,
                        id_,
                        updated,
                        rc,
                        offsets,
                        page_url,
                        up_url,
                        title=feed_title).root
Ejemplo n.º 3
0
def opds_category(ctx, rd, category, which):
    try:
        offset = int(rd.query.get('offset', 0))
    except Exception:
        raise HTTPNotFound('Not found')

    if not which or not category:
        raise HTTPNotFound('Not found')
    rc = RequestContext(ctx, rd)
    page_url = rc.url_for('/opds/category', which=which, category=category)
    up_url = rc.url_for('/opds/navcatalog', which=category)

    which, category = from_hex_unicode(which), from_hex_unicode(category)
    type_ = which[0]
    which = which[1:]
    if type_ == 'I':
        try:
            p = which.rindex(':')
            category = which[p+1:]
            which = which[:p]
            # This line will toss an exception for composite columns
            which = int(which[:p])
        except Exception:
            # Might be a composite column, where we have the lookup key
            if not (category in rc.db.field_metadata and rc.db.field_metadata[category]['datatype'] == 'composite'):
                raise HTTPNotFound('Tag %r not found'%which)

    categories = rc.get_categories()
    if category not in categories:
        raise HTTPNotFound('Category %r not found'%which)

    if category == 'search':
        try:
            ids = rc.search('search:"%s"'%which)
        except Exception:
            raise HTTPNotFound('Search: %r not understood'%which)
        return get_acquisition_feed(rc, ids, offset, page_url, up_url, 'calibre-search:'+which)

    if type_ != 'I':
        raise HTTPNotFound('Non id categories not supported')

    q = category
    if q == 'news':
        q = 'tags'
    ids = rc.db.get_books_for_category(q, which)
    sort_by = 'series' if category == 'series' else 'title'

    return get_acquisition_feed(rc, ids, offset, page_url, up_url, 'calibre-category:'+category+':'+str(which), sort_by=sort_by)
Ejemplo n.º 4
0
    def sendmail(self, attachment, aname, to, subject, text, log):
        logged = False
        while time.time() - self.last_send_time <= self.rate_limit:
            if not logged and self.rate_limit > 0:
                log('Waiting %s seconds before sending, to avoid being marked as spam.\nYou can control this delay via Preferences->Tweaks' % self.rate_limit)
                logged = True
            time.sleep(1)
        try:
            opts = email_config().parse()
            from_ = opts.from_
            if not from_:
                from_ = 'calibre <calibre@'+socket.getfqdn()+'>'
            with lopen(attachment, 'rb') as f:
                msg = compose_mail(from_, to, text, subject, f, aname)
            efrom = extract_email_address(from_)
            eto = []
            for x in to.split(','):
                eto.append(extract_email_address(x.strip()))

            def safe_debug(*args, **kwargs):
                try:
                    return log.debug(*args, **kwargs)
                except Exception:
                    pass

            sendmail(msg, efrom, eto, localhost=None,
                        verbose=1,
                        relay=opts.relay_host,
                        username=opts.relay_username,
                        password=from_hex_unicode(opts.relay_password), port=opts.relay_port,
                        encryption=opts.encryption,
                        debug_output=safe_debug)
        finally:
            self.last_send_time = time.time()
Ejemplo n.º 5
0
    def sendmail(self, attachment, aname, to, subject, text, log):
        logged = False
        while time.time() - self.last_send_time <= self.rate_limit:
            if not logged and self.rate_limit > 0:
                log('Waiting %s seconds before sending, to avoid being marked as spam.\nYou can control this delay via Preferences->Tweaks' % self.rate_limit)
                logged = True
            time.sleep(1)
        try:
            opts = email_config().parse()
            from_ = opts.from_
            if not from_:
                from_ = 'calibre <calibre@'+socket.getfqdn()+'>'
            with lopen(attachment, 'rb') as f:
                msg = compose_mail(from_, to, text, subject, f, aname)
            efrom = extract_email_address(from_)
            eto = []
            for x in to.split(','):
                eto.append(extract_email_address(x.strip()))

            def safe_debug(*args, **kwargs):
                try:
                    return log.debug(*args, **kwargs)
                except Exception:
                    pass

            sendmail(msg, efrom, eto, localhost=None,
                        verbose=1,
                        relay=opts.relay_host,
                        username=opts.relay_username,
                        password=from_hex_unicode(opts.relay_password), port=opts.relay_port,
                        encryption=opts.encryption,
                        debug_output=safe_debug)
        finally:
            self.last_send_time = time.time()
Ejemplo n.º 6
0
 def __init__(self, pa, parent):
     QDialog.__init__(self, parent)
     self.test_func = parent.test_email_settings
     self.setWindowTitle(_("Test email settings"))
     self.setWindowIcon(QIcon(I('config.ui')))
     l = QVBoxLayout(self)
     opts = smtp_prefs().parse()
     self.from_ = la = QLabel(_("Send test mail from %s to:")%opts.from_)
     l.addWidget(la)
     self.to = le = QLineEdit(self)
     if pa:
         self.to.setText(pa)
     self.test_button = b = QPushButton(_('&Test'), self)
     b.clicked.connect(self.start_test)
     self.test_done.connect(self.on_test_done, type=Qt.QueuedConnection)
     self.h = h = QHBoxLayout()
     h.addWidget(le), h.addWidget(b)
     l.addLayout(h)
     if opts.relay_host:
         self.la = la = QLabel(_('Using: %(un)s:%(pw)s@%(host)s:%(port)s and %(enc)s encryption')%
                 dict(un=opts.relay_username, pw=from_hex_unicode(opts.relay_password),
                     host=opts.relay_host, port=opts.relay_port, enc=opts.encryption))
         l.addWidget(la)
     self.log = QPlainTextEdit(self)
     l.addWidget(self.log)
     self.bb = bb = QDialogButtonBox(QDialogButtonBox.Close)
     bb.rejected.connect(self.reject), bb.accepted.connect(self.accept)
     l.addWidget(bb)
Ejemplo n.º 7
0
    def initialize(self, preferred_to_address):
        self.preferred_to_address = preferred_to_address
        opts = smtp_prefs().parse()
        self.smtp_opts = opts
        if opts.from_:
            self.email_from.setText(opts.from_)
        self.email_from.textChanged.connect(self.changed)
        if opts.relay_host:
            self.relay_host.setText(opts.relay_host)
        self.relay_host.textChanged.connect(self.changed)
        self.relay_port.setValue(opts.relay_port)
        self.relay_port.valueChanged.connect(self.changed)
        if opts.relay_username:
            self.relay_username.setText(opts.relay_username)
        self.relay_username.textChanged.connect(self.changed)
        if opts.relay_password:
            self.relay_password.setText(from_hex_unicode(opts.relay_password))
        self.relay_password.textChanged.connect(self.changed)
        getattr(self, 'relay_'+opts.encryption.lower()).setChecked(True)
        self.relay_tls.toggled.connect(self.changed)

        for x in ('gmx', 'hotmail'):
            button = getattr(self, 'relay_use_'+x)
            button.clicked.connect(partial(self.create_service_relay, x))
        self.relay_show_password.stateChanged.connect(
         lambda state : self.relay_password.setEchoMode(
             self.relay_password.Password if
             state == 0 else self.relay_password.Normal))
        self.test_email_button.clicked.connect(self.test_email)
Ejemplo n.º 8
0
    def initialize(self, preferred_to_address):
        self.preferred_to_address = preferred_to_address
        opts = smtp_prefs().parse()
        self.smtp_opts = opts
        if opts.from_:
            self.email_from.setText(opts.from_)
        self.email_from.textChanged.connect(self.changed)
        if opts.relay_host:
            self.relay_host.setText(opts.relay_host)
        self.relay_host.textChanged.connect(self.changed)
        self.relay_port.setValue(opts.relay_port)
        self.relay_port.valueChanged.connect(self.changed)
        if opts.relay_username:
            self.relay_username.setText(opts.relay_username)
        self.relay_username.textChanged.connect(self.changed)
        if opts.relay_password:
            self.relay_password.setText(from_hex_unicode(opts.relay_password))
        self.relay_password.textChanged.connect(self.changed)
        getattr(self, 'relay_'+opts.encryption.lower()).setChecked(True)
        self.relay_tls.toggled.connect(self.changed)

        for x in ('gmx', 'hotmail'):
            button = getattr(self, 'relay_use_'+x)
            button.clicked.connect(partial(self.create_service_relay, x))
        self.relay_show_password.stateChanged.connect(
         lambda state : self.relay_password.setEchoMode(
             self.relay_password.Password if
             state == 0 else self.relay_password.Normal))
        self.test_email_button.clicked.connect(self.test_email)
Ejemplo n.º 9
0
 def __init__(self, pa, parent):
     QDialog.__init__(self, parent)
     self.test_func = parent.test_email_settings
     self.setWindowTitle(_("Test email settings"))
     self.setWindowIcon(QIcon(I('config.ui')))
     l = QVBoxLayout(self)
     opts = smtp_prefs().parse()
     self.from_ = la = QLabel(_("Send test mail from %s to:")%opts.from_)
     l.addWidget(la)
     self.to = le = QLineEdit(self)
     if pa:
         self.to.setText(pa)
     self.test_button = b = QPushButton(_('&Test'), self)
     b.clicked.connect(self.start_test)
     self.test_done.connect(self.on_test_done, type=Qt.QueuedConnection)
     self.h = h = QHBoxLayout()
     h.addWidget(le), h.addWidget(b)
     l.addLayout(h)
     if opts.relay_host:
         self.la = la = QLabel(_('Using: %(un)s:%(pw)s@%(host)s:%(port)s and %(enc)s encryption')%
                 dict(un=opts.relay_username, pw=from_hex_unicode(opts.relay_password),
                     host=opts.relay_host, port=opts.relay_port, enc=opts.encryption))
         l.addWidget(la)
     self.log = QPlainTextEdit(self)
     l.addWidget(self.log)
     self.bb = bb = QDialogButtonBox(QDialogButtonBox.Close)
     bb.rejected.connect(self.reject), bb.accepted.connect(self.accept)
     l.addWidget(bb)
Ejemplo n.º 10
0
 def test_email_settings(self, to):
     opts = smtp_prefs().parse()
     from calibre.utils.smtp import sendmail, create_mail
     buf = PolyglotStringIO()
     debug_out = partial(prints, file=buf)
     oout, oerr = sys.stdout, sys.stderr
     sys.stdout = sys.stderr = buf
     tb = None
     try:
         msg = create_mail(opts.from_, to, 'Test mail from calibre',
                           'Test mail from calibre')
         sendmail(msg,
                  from_=opts.from_,
                  to=[to],
                  verbose=3,
                  timeout=30,
                  relay=opts.relay_host,
                  username=opts.relay_username,
                  debug_output=debug_out,
                  password=from_hex_unicode(opts.relay_password),
                  encryption=opts.encryption,
                  port=opts.relay_port)
     except:
         import traceback
         tb = traceback.format_exc()
         tb += '\n\nLog:\n' + buf.getvalue()
     finally:
         sys.stdout, sys.stderr = oout, oerr
     return tb
Ejemplo n.º 11
0
def opds_categorygroup(ctx, rd, category, which):
    try:
        offset = int(rd.query.get('offset', 0))
    except Exception:
        raise HTTPNotFound('Not found')

    if not which or not category:
        raise HTTPNotFound('Not found')

    rc = RequestContext(ctx, rd)
    categories = rc.get_categories()
    page_url = rc.url_for('/opds/categorygroup', category=category, which=which)

    category = from_hex_unicode(category)
    if category not in categories:
        raise HTTPNotFound('Category %r not found'%which)
    category_meta = rc.db.field_metadata
    meta = category_meta.get(category, {})
    category_name = meta.get('name', which)
    which = from_hex_unicode(which)
    feed_title = default_feed_title + ' :: ' + (_('By {0} :: {1}').format(category_name, which))
    owhich = as_hex_unicode('N'+which)
    up_url = rc.url_for('/opds/navcatalog', which=owhich)
    items = categories[category]

    def belongs(x, which):
        return getattr(x, 'sort', x.name).lower().startswith(which.lower())
    items = [x for x in items if belongs(x, which)]
    if not items:
        raise HTTPNotFound('No items in group %r:%r'%(category, which))
    updated = rc.last_modified()

    id_ = 'calibre-category-group-feed:'+category+':'+which

    max_items = rc.opts.max_opds_items
    offsets = Offsets(offset, max_items, len(items))
    items = list(items)[offsets.offset:offsets.offset+max_items]

    rc.outheaders['Last-Modified'] = http_date(timestampfromdt(updated))

    return CategoryFeed(items, category, id_, updated, rc, offsets, page_url, up_url, title=feed_title).root
Ejemplo n.º 12
0
 def handle_click(self, link):
     typ, val = link.partition(':')[0::2]
     if typ == 'path':
         self.open_containing_folder.emit(int(val))
     elif typ == 'format':
         id_, fmt = val.split(':')
         self.view_specific_format.emit(int(id_), fmt)
     elif typ == 'devpath':
         self.view_device_book.emit(val)
     elif typ == 'search':
         self.search_requested.emit(from_hex_unicode(val))
     else:
         try:
             safe_open_url(QUrl(link, QUrl.TolerantMode))
         except:
             import traceback
             traceback.print_exc()
Ejemplo n.º 13
0
def opds_navcatalog(ctx, rd, which):
    try:
        offset = int(rd.query.get('offset', 0))
    except Exception:
        raise HTTPNotFound('Not found')
    rc = RequestContext(ctx, rd)

    page_url = rc.url_for('/opds/navcatalog', which=which)
    up_url = rc.url_for('/opds')
    which = from_hex_unicode(which)
    type_ = which[0]
    which = which[1:]
    if type_ == 'O':
        return get_all_books(rc, which, page_url, up_url, offset=offset)
    elif type_ == 'N':
        return get_navcatalog(rc, which, page_url, up_url, offset=offset)
    raise HTTPNotFound('Not found')
Ejemplo n.º 14
0
 def handle_click(self, link):
     typ, val = link.partition(':')[0::2]
     if typ == 'path':
         self.open_containing_folder.emit(int(val))
     elif typ == 'format':
         id_, fmt = val.split(':')
         self.view_specific_format.emit(int(id_), fmt)
     elif typ == 'devpath':
         self.view_device_book.emit(val)
     elif typ == 'search':
         self.search_requested.emit(from_hex_unicode(val))
     else:
         try:
             open_url(QUrl(link, QUrl.TolerantMode))
         except:
             import traceback
             traceback.print_exc()
Ejemplo n.º 15
0
def opds_navcatalog(ctx, rd, which):
    try:
        offset = int(rd.query.get('offset', 0))
    except Exception:
        raise HTTPNotFound('Not found')
    rc = RequestContext(ctx, rd)

    page_url = rc.url_for('/opds/navcatalog', which=which)
    up_url = rc.url_for('/opds')
    which = from_hex_unicode(which)
    type_ = which[0]
    which = which[1:]
    if type_ == 'O':
        return get_all_books(rc, which, page_url, up_url, offset=offset)
    elif type_ == 'N':
        return get_navcatalog(rc, which, page_url, up_url, offset=offset)
    raise HTTPNotFound('Not found')
Ejemplo n.º 16
0
 def test_email_settings(self, to):
     opts = smtp_prefs().parse()
     from calibre.utils.smtp import sendmail, create_mail
     buf = PolyglotBytesIO()
     debug_out = partial(prints, file=buf)
     oout, oerr = sys.stdout, sys.stderr
     sys.stdout = sys.stderr = buf
     tb = None
     try:
         msg = create_mail(opts.from_, to, 'Test mail from calibre',
                 'Test mail from calibre')
         sendmail(msg, from_=opts.from_, to=[to],
             verbose=3, timeout=30, relay=opts.relay_host,
             username=opts.relay_username, debug_output=debug_out,
             password=from_hex_unicode(opts.relay_password),
             encryption=opts.encryption, port=opts.relay_port)
     except:
         import traceback
         tb = traceback.format_exc()
         tb += '\n\nLog:\n' + buf.getvalue().decode('utf-8', 'replace')
     finally:
         sys.stdout, sys.stderr = oout, oerr
     return tb
Ejemplo n.º 17
0
def main():
    if iswindows:
        if '--multiprocessing-fork' in sys.argv:
            # We are using the multiprocessing module on windows to launch a
            # worker process
            from multiprocessing import freeze_support
            freeze_support()
            return 0
    if ismacos and 'CALIBRE_WORKER_FD' not in os.environ and 'CALIBRE_SIMPLE_WORKER' not in os.environ and '--pipe-worker' not in sys.argv:
        # On some OS X computers launchd apparently tries to
        # launch the last run process from the bundle
        # so launch the gui as usual
        from calibre.gui2.main import main as gui_main
        return gui_main(['calibre'])
    niceness = os.environ.pop('CALIBRE_WORKER_NICENESS', None)
    if niceness:
        try:
            os.nice(int(niceness))
        except Exception:
            pass
    csw = os.environ.pop('CALIBRE_SIMPLE_WORKER', None)
    if csw:
        mod, _, func = csw.partition(':')
        mod = importlib.import_module(mod)
        func = getattr(mod, func)
        func()
        return
    if '--pipe-worker' in sys.argv:
        try:
            exec(sys.argv[-1])
        except Exception:
            print('Failed to run pipe worker with command:', sys.argv[-1])
            sys.stdout.flush()
            raise
        return
    fd = int(os.environ['CALIBRE_WORKER_FD'])
    resultf = from_hex_unicode(os.environ['CALIBRE_WORKER_RESULT'])
    with Connection(fd) as conn:
        name, args, kwargs, desc = eintr_retry_call(conn.recv)
        if desc:
            prints(desc)
            sys.stdout.flush()
        func, notification = get_func(name)
        notifier = Progress(conn)
        if notification:
            kwargs[notification] = notifier
            notifier.start()

        result = func(*args, **kwargs)
        if result is not None:
            os.makedirs(os.path.dirname(resultf), exist_ok=True)
            with lopen(resultf, 'wb') as f:
                f.write(pickle_dumps(result))

        notifier.queue.put(None)

    try:
        sys.stdout.flush()
    except OSError:
        pass  # Happens sometimes on OS X for GUI processes (EPIPE)
    try:
        sys.stderr.flush()
    except OSError:
        pass  # Happens sometimes on OS X for GUI processes (EPIPE)
    return 0
Ejemplo n.º 18
0
    def test_get(self):  # {{{
        'Test /get'
        with self.create_server() as server:
            db = server.handler.router.ctx.library_broker.get(None)
            conn = server.connect()

            def get(what, book_id, library_id=None, q=''):
                q = ('?' + q) if q else q
                conn.request(
                    'GET', '/get/%s/%s' % (what, book_id) +
                    (('/' + library_id) if library_id else '') + q)
                r = conn.getresponse()
                return r, r.read()

            # Test various invalid parameters
            def bad(*args):
                r, data = get(*args)
                self.ae(r.status, http_client.NOT_FOUND)

            bad('xxx', 1)
            bad('fmt1', 10)
            bad('fmt1', 1, 'zzzz')
            bad('fmt1', 'xx')

            # Test simple fetching of format without metadata update
            r, data = get('fmt1', 1, db.server_library_id)
            self.ae(data, db.format(1, 'fmt1'))
            self.assertIsNotNone(r.getheader('Content-Disposition'))
            self.ae(r.getheader('Used-Cache'), 'no')
            r, data = get('fmt1', 1)
            self.ae(data, db.format(1, 'fmt1'))
            self.ae(r.getheader('Used-Cache'), 'yes')

            # Test fetching of format with metadata update
            raw = P('quick_start/eng.epub', data=True)
            r, data = get('epub', 1)
            self.ae(r.status, http_client.OK)
            etag = r.getheader('ETag')
            self.assertIsNotNone(etag)
            self.ae(r.getheader('Used-Cache'), 'no')
            self.assertTrue(data.startswith(b'PK'))
            self.assertGreaterEqual(len(data), len(raw))
            db.set_field('title', {1: 'changed'})
            r, data = get('epub', 1)
            self.assertNotEqual(r.getheader('ETag'), etag)
            etag = r.getheader('ETag')
            self.ae(r.getheader('Used-Cache'), 'no')
            mi = get_metadata(BytesIO(data), extract_cover=False)
            self.ae(mi.title, 'changed')
            r, data = get('epub', 1)
            self.ae(r.getheader('Used-Cache'), 'yes')

            # Test plugboards
            import calibre.library.save_to_disk as c
            orig, c.DEBUG = c.DEBUG, False
            try:
                db.set_pref(
                    'plugboards', {
                        u'epub': {
                            u'content_server':
                            [[u'changed, {title}', u'title']]
                        }
                    })
                # this is needed as the cache is not invalidated for plugboard changes
                db.set_field('title', {1: 'again'})
                r, data = get('epub', 1)
                self.assertNotEqual(r.getheader('ETag'), etag)
                etag = r.getheader('ETag')
                self.ae(r.getheader('Used-Cache'), 'no')
                mi = get_metadata(BytesIO(data), extract_cover=False)
                self.ae(mi.title, 'changed, again')
            finally:
                c.DEBUG = orig

            # Test the serving of covers
            def change_cover(count, book_id=2):
                cpath = db.format_abspath(book_id, '__COVER_INTERNAL__')
                db.set_cover({2: I('lt.png', data=True)})
                t = time.time() + 1 + count
                # Ensure mtime changes, needed on OS X where HFS+ has a 1s
                # mtime resolution
                os.utime(cpath, (t, t))

            r, data = get('cover', 1)
            self.ae(r.status, http_client.OK)
            self.ae(data, db.cover(1))
            self.ae(r.getheader('Used-Cache'), 'no')
            self.ae(r.getheader('Content-Type'), 'image/jpeg')
            r, data = get('cover', 1)
            self.ae(r.status, http_client.OK)
            self.ae(data, db.cover(1))
            self.ae(r.getheader('Used-Cache'), 'yes')
            r, data = get('cover', 3)
            self.ae(r.status, http_client.OK)  # Auto generated cover
            r, data = get('thumb', 1)
            self.ae(r.status, http_client.OK)
            self.ae(identify(data), ('jpeg', 60, 60))
            self.ae(r.getheader('Used-Cache'), 'no')
            r, data = get('thumb', 1)
            self.ae(r.status, http_client.OK)
            self.ae(r.getheader('Used-Cache'), 'yes')
            r, data = get('thumb', 1, q='sz=100')
            self.ae(r.status, http_client.OK)
            self.ae(identify(data), ('jpeg', 100, 100))
            self.ae(r.getheader('Used-Cache'), 'no')
            r, data = get('thumb', 1, q='sz=100x100')
            self.ae(r.status, http_client.OK)
            self.ae(r.getheader('Used-Cache'), 'yes')
            change_cover(1, 1)
            r, data = get('thumb', 1, q='sz=100')
            self.ae(r.status, http_client.OK)
            self.ae(identify(data), ('jpeg', 100, 100))
            self.ae(r.getheader('Used-Cache'), 'no')

            # Test file sharing in cache
            r, data = get('cover', 2)
            self.ae(r.status, http_client.OK)
            self.ae(data, db.cover(2))
            self.ae(r.getheader('Used-Cache'), 'no')
            path = from_hex_unicode(r.getheader('Tempfile'))
            f, fdata = share_open(path, 'rb'), data
            # Now force an update
            change_cover(1)
            r, data = get('cover', 2)
            self.ae(r.status, http_client.OK)
            self.ae(data, db.cover(2))
            self.ae(r.getheader('Used-Cache'), 'no')
            path = from_hex_unicode(r.getheader('Tempfile'))
            f2, f2data = share_open(path, 'rb'), data
            # Do it again
            change_cover(2)
            r, data = get('cover', 2)
            self.ae(r.status, http_client.OK)
            self.ae(data, db.cover(2))
            self.ae(r.getheader('Used-Cache'), 'no')
            self.ae(f.read(), fdata)
            self.ae(f2.read(), f2data)

            # Test serving of metadata as opf
            r, data = get('opf', 1)
            self.ae(r.status, http_client.OK)
            self.ae(r.getheader('Content-Type'),
                    'application/oebps-package+xml; charset=UTF-8')
            self.assertIsNotNone(r.getheader('Last-Modified'))
            opf = OPF(BytesIO(data),
                      populate_spine=False,
                      try_to_guess_cover=False)
            self.ae(db.field_for('title', 1), opf.title)
            self.ae(db.field_for('authors', 1), tuple(opf.authors))
            conn.request('GET',
                         '/get/opf/1',
                         headers={'Accept-Encoding': 'gzip'})
            r = conn.getresponse()
            self.ae(r.status,
                    http_client.OK), self.ae(r.getheader('Content-Encoding'),
                                             'gzip')
            raw = r.read()
            self.ae(zlib.decompress(raw, 16 + zlib.MAX_WBITS), data)

            # Test serving metadata as json
            r, data = get('json', 1)
            self.ae(r.status, http_client.OK)
            self.ae(db.field_for('title', 1), json.loads(data)['title'])
            conn.request('GET',
                         '/get/json/1',
                         headers={'Accept-Encoding': 'gzip'})
            r = conn.getresponse()
            self.ae(r.status,
                    http_client.OK), self.ae(r.getheader('Content-Encoding'),
                                             'gzip')
            raw = r.read()
            self.ae(zlib.decompress(raw, 16 + zlib.MAX_WBITS), data)
Ejemplo n.º 19
0
    def test_get(self):  # {{{
        'Test /get'
        with self.create_server() as server:
            db = server.handler.router.ctx.library_broker.get(None)
            conn = server.connect()

            def get(what, book_id, library_id=None, q=''):
                q = ('?' + q) if q else q
                conn.request('GET', '/get/%s/%s' % (what, book_id) + (('/' + library_id) if library_id else '') + q)
                r = conn.getresponse()
                return r, r.read()

            # Test various invalid parameters
            def bad(*args):
                r, data = get(*args)
                self.ae(r.status, http_client.NOT_FOUND)
            bad('xxx', 1)
            bad('fmt1', 10)
            bad('fmt1', 1, 'zzzz')
            bad('fmt1', 'xx')

            # Test simple fetching of format without metadata update
            r, data = get('fmt1', 1, db.server_library_id)
            self.ae(data, db.format(1, 'fmt1'))
            self.assertIsNotNone(r.getheader('Content-Disposition'))
            self.ae(r.getheader('Used-Cache'), 'no')
            r, data = get('fmt1', 1)
            self.ae(data, db.format(1, 'fmt1'))
            self.ae(r.getheader('Used-Cache'), 'yes')

            # Test fetching of format with metadata update
            raw = P('quick_start/eng.epub', data=True)
            r, data = get('epub', 1)
            self.ae(r.status, http_client.OK)
            etag = r.getheader('ETag')
            self.assertIsNotNone(etag)
            self.ae(r.getheader('Used-Cache'), 'no')
            self.assertTrue(data.startswith(b'PK'))
            self.assertGreaterEqual(len(data), len(raw))
            db.set_field('title', {1:'changed'})
            r, data = get('epub', 1)
            self.assertNotEqual(r.getheader('ETag'), etag)
            etag = r.getheader('ETag')
            self.ae(r.getheader('Used-Cache'), 'no')
            mi = get_metadata(BytesIO(data), extract_cover=False)
            self.ae(mi.title, 'changed')
            r, data = get('epub', 1)
            self.ae(r.getheader('Used-Cache'), 'yes')

            # Test plugboards
            import calibre.library.save_to_disk as c
            orig, c.DEBUG = c.DEBUG, False
            try:
                db.set_pref('plugboards', {u'epub': {u'content_server': [[u'changed, {title}', u'title']]}})
                # this is needed as the cache is not invalidated for plugboard changes
                db.set_field('title', {1:'again'})
                r, data = get('epub', 1)
                self.assertNotEqual(r.getheader('ETag'), etag)
                etag = r.getheader('ETag')
                self.ae(r.getheader('Used-Cache'), 'no')
                mi = get_metadata(BytesIO(data), extract_cover=False)
                self.ae(mi.title, 'changed, again')
            finally:
                c.DEBUG = orig

            # Test the serving of covers
            def change_cover(count, book_id=2):
                cpath = db.format_abspath(book_id, '__COVER_INTERNAL__')
                db.set_cover({2:I('lt.png', data=True)})
                t = time.time() + 1 + count
                # Ensure mtime changes, needed on OS X where HFS+ has a 1s
                # mtime resolution
                os.utime(cpath, (t, t))

            r, data = get('cover', 1)
            self.ae(r.status, http_client.OK)
            self.ae(data, db.cover(1))
            self.ae(r.getheader('Used-Cache'), 'no')
            self.ae(r.getheader('Content-Type'), 'image/jpeg')
            r, data = get('cover', 1)
            self.ae(r.status, http_client.OK)
            self.ae(data, db.cover(1))
            self.ae(r.getheader('Used-Cache'), 'yes')
            r, data = get('cover', 3)
            self.ae(r.status, http_client.OK)  # Auto generated cover
            r, data = get('thumb', 1)
            self.ae(r.status, http_client.OK)
            self.ae(identify(data), ('jpeg', 60, 60))
            self.ae(r.getheader('Used-Cache'), 'no')
            r, data = get('thumb', 1)
            self.ae(r.status, http_client.OK)
            self.ae(r.getheader('Used-Cache'), 'yes')
            r, data = get('thumb', 1, q='sz=100')
            self.ae(r.status, http_client.OK)
            self.ae(identify(data), ('jpeg', 100, 100))
            self.ae(r.getheader('Used-Cache'), 'no')
            r, data = get('thumb', 1, q='sz=100x100')
            self.ae(r.status, http_client.OK)
            self.ae(r.getheader('Used-Cache'), 'yes')
            change_cover(1, 1)
            r, data = get('thumb', 1, q='sz=100')
            self.ae(r.status, http_client.OK)
            self.ae(identify(data), ('jpeg', 100, 100))
            self.ae(r.getheader('Used-Cache'), 'no')

            # Test file sharing in cache
            r, data = get('cover', 2)
            self.ae(r.status, http_client.OK)
            self.ae(data, db.cover(2))
            self.ae(r.getheader('Used-Cache'), 'no')
            path = from_hex_unicode(r.getheader('Tempfile'))
            f, fdata = share_open(path, 'rb'), data
            # Now force an update
            change_cover(1)
            r, data = get('cover', 2)
            self.ae(r.status, http_client.OK)
            self.ae(data, db.cover(2))
            self.ae(r.getheader('Used-Cache'), 'no')
            path = from_hex_unicode(r.getheader('Tempfile'))
            f2, f2data = share_open(path, 'rb'), data
            # Do it again
            change_cover(2)
            r, data = get('cover', 2)
            self.ae(r.status, http_client.OK)
            self.ae(data, db.cover(2))
            self.ae(r.getheader('Used-Cache'), 'no')
            self.ae(f.read(), fdata)
            self.ae(f2.read(), f2data)

            # Test serving of metadata as opf
            r, data = get('opf', 1)
            self.ae(r.status, http_client.OK)
            self.ae(r.getheader('Content-Type'), 'application/oebps-package+xml; charset=UTF-8')
            self.assertIsNotNone(r.getheader('Last-Modified'))
            opf = OPF(BytesIO(data), populate_spine=False, try_to_guess_cover=False)
            self.ae(db.field_for('title', 1), opf.title)
            self.ae(db.field_for('authors', 1), tuple(opf.authors))
            conn.request('GET', '/get/opf/1', headers={'Accept-Encoding':'gzip'})
            r = conn.getresponse()
            self.ae(r.status, http_client.OK), self.ae(r.getheader('Content-Encoding'), 'gzip')
            raw = r.read()
            self.ae(zlib.decompress(raw, 16+zlib.MAX_WBITS), data)

            # Test serving metadata as json
            r, data = get('json', 1)
            self.ae(r.status, http_client.OK)
            self.ae(db.field_for('title', 1), json.loads(data)['title'])
            conn.request('GET', '/get/json/1', headers={'Accept-Encoding':'gzip'})
            r = conn.getresponse()
            self.ae(r.status, http_client.OK), self.ae(r.getheader('Content-Encoding'), 'gzip')
            raw = r.read()
            self.ae(zlib.decompress(raw, 16+zlib.MAX_WBITS), data)
Ejemplo n.º 20
0
def main():
    if iswindows:
        if '--multiprocessing-fork' in sys.argv:
            # We are using the multiprocessing module on windows to launch a
            # worker process
            from multiprocessing import freeze_support
            freeze_support()
            return 0
        # Close open file descriptors inherited from parent
        # On Unix this is done by the subprocess module
        os.closerange(3, 256)
    if isosx and 'CALIBRE_WORKER_ADDRESS' not in os.environ and 'CALIBRE_SIMPLE_WORKER' not in os.environ and '--pipe-worker' not in sys.argv:
        # On some OS X computers launchd apparently tries to
        # launch the last run process from the bundle
        # so launch the gui as usual
        from calibre.gui2.main import main as gui_main
        return gui_main(['calibre'])
    csw = os.environ.get('CALIBRE_SIMPLE_WORKER', None)
    if csw:
        mod, _, func = csw.partition(':')
        mod = importlib.import_module(mod)
        func = getattr(mod, func)
        func()
        return
    if '--pipe-worker' in sys.argv:
        try:
            exec (sys.argv[-1])
        except Exception:
            print('Failed to run pipe worker with command:', sys.argv[-1])
            raise
        return
    address = msgpack_loads(from_hex_bytes(os.environ['CALIBRE_WORKER_ADDRESS']))
    key     = from_hex_bytes(os.environ['CALIBRE_WORKER_KEY'])
    resultf = from_hex_unicode(os.environ['CALIBRE_WORKER_RESULT'])
    with closing(Client(address, authkey=key)) as conn:
        name, args, kwargs, desc = eintr_retry_call(conn.recv)
        if desc:
            prints(desc)
            sys.stdout.flush()
        func, notification = get_func(name)
        notifier = Progress(conn)
        if notification:
            kwargs[notification] = notifier
            notifier.start()

        result = func(*args, **kwargs)
        if result is not None and os.path.exists(os.path.dirname(resultf)):
            with lopen(resultf, 'wb') as f:
                f.write(pickle_dumps(result))

        notifier.queue.put(None)

    try:
        sys.stdout.flush()
    except EnvironmentError:
        pass  # Happens sometimes on OS X for GUI processes (EPIPE)
    try:
        sys.stderr.flush()
    except EnvironmentError:
        pass  # Happens sometimes on OS X for GUI processes (EPIPE)
    return 0
Ejemplo n.º 21
0
def main():
    if iswindows:
        if '--multiprocessing-fork' in sys.argv:
            # We are using the multiprocessing module on windows to launch a
            # worker process
            from multiprocessing import freeze_support
            freeze_support()
            return 0
        # Close open file descriptors inherited from parent
        # On Unix this is done by the subprocess module
        os.closerange(3, 256)
    if isosx and 'CALIBRE_WORKER_ADDRESS' not in os.environ and 'CALIBRE_SIMPLE_WORKER' not in os.environ and '--pipe-worker' not in sys.argv:
        # On some OS X computers launchd apparently tries to
        # launch the last run process from the bundle
        # so launch the gui as usual
        from calibre.gui2.main import main as gui_main
        return gui_main(['calibre'])
    csw = os.environ.get('CALIBRE_SIMPLE_WORKER', None)
    if csw:
        mod, _, func = csw.partition(':')
        mod = importlib.import_module(mod)
        func = getattr(mod, func)
        func()
        return
    if '--pipe-worker' in sys.argv:
        try:
            exec(sys.argv[-1])
        except Exception:
            print('Failed to run pipe worker with command:', sys.argv[-1])
            raise
        return
    address = msgpack_loads(from_hex_bytes(os.environ['CALIBRE_WORKER_ADDRESS']))
    key     = from_hex_bytes(os.environ['CALIBRE_WORKER_KEY'])
    resultf = from_hex_unicode(os.environ['CALIBRE_WORKER_RESULT'])
    with closing(Client(address, authkey=key)) as conn:
        name, args, kwargs, desc = eintr_retry_call(conn.recv)
        if desc:
            prints(desc)
            sys.stdout.flush()
        func, notification = get_func(name)
        notifier = Progress(conn)
        if notification:
            kwargs[notification] = notifier
            notifier.start()

        result = func(*args, **kwargs)
        if result is not None and os.path.exists(os.path.dirname(resultf)):
            with lopen(resultf, 'wb') as f:
                f.write(pickle_dumps(result))

        notifier.queue.put(None)

    try:
        sys.stdout.flush()
    except EnvironmentError:
        pass  # Happens sometimes on OS X for GUI processes (EPIPE)
    try:
        sys.stderr.flush()
    except EnvironmentError:
        pass  # Happens sometimes on OS X for GUI processes (EPIPE)
    return 0